Skip to main content
Glama
ooples

MCP Console Automation Server

sftp.md39.5 kB
# SFTP Protocol ## Overview The SFTP Protocol enables AI assistants to securely transfer files, manage remote directories, and perform file operations over SSH connections through the MCP Console Automation server. It provides comprehensive secure file management capabilities with support for various authentication methods and advanced features. ## Features - **Secure File Transfer**: Encrypted file transfers over SSH - **Multiple Authentication**: Password, key-based, and agent authentication - **Directory Management**: Create, list, and manage remote directories - **File Operations**: Upload, download, rename, delete, and set permissions - **Batch Operations**: Multiple file transfers with progress tracking - **Resume Support**: Resume interrupted transfers - **Compression**: Optional data compression for faster transfers - **Symbolic Links**: Support for symbolic link operations - **File Integrity**: Checksum verification for transferred files ## Prerequisites - SSH server with SFTP subsystem enabled - Network connectivity to target server - Valid authentication credentials - Appropriate file system permissions ### Server Requirements #### Linux/Unix SFTP Server ```bash # Install OpenSSH server sudo apt-get install openssh-server # Enable SFTP subsystem in /etc/ssh/sshd_config Subsystem sftp /usr/lib/openssh/sftp-server # Optional: Restrict users to SFTP only Match User sftpuser ForceCommand internal-sftp PasswordAuthentication yes ChrootDirectory /var/sftp PermitTunnel no AllowAgentForwarding no AllowTcpForwarding no X11Forwarding no sudo systemctl restart ssh ``` #### Windows SFTP Server - Install OpenSSH Server feature - Or use third-party solutions like FileZilla Server, Bitvise SSH Server ## Configuration ### Basic Configuration ```typescript const sftpConfig: SFTPProtocolConfig = { connection: { host: 'sftp.example.com', port: 22, username: 'user', password: 'password', // or use privateKey // privateKey: require('fs').readFileSync('/path/to/private/key'), // passphrase: 'key-passphrase' }, options: { keepAliveInterval: 60000, readyTimeout: 30000, strictVendorChecking: false, compression: true, algorithms: { kex: ['diffie-hellman-group14-sha256', 'ecdh-sha2-nistp256'], cipher: ['aes128-gcm', 'aes256-gcm', 'aes128-ctr', 'aes256-ctr'], serverHostKey: ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-rsa'], hmac: ['hmac-sha2-256', 'hmac-sha2-512', 'hmac-sha1'] } }, transfer: { concurrency: 3, chunkSize: 32768, // 32KB preserveTimestamps: true, preservePermissions: true, resumeSupport: true, checksumVerification: true } }; ``` ### Advanced Configuration ```typescript const advancedSFTPConfig: SFTPProtocolConfig = { connection: { host: 'secure-server.example.com', port: 2222, username: 'deployuser', privateKey: require('fs').readFileSync('/home/user/.ssh/id_rsa'), passphrase: process.env.SSH_KEY_PASSPHRASE, agent: process.env.SSH_AUTH_SOCK, // Use SSH agent agentForward: true }, security: { hostVerification: true, knownHostsFile: '/home/user/.ssh/known_hosts', strictHostKeyChecking: true, allowInsecureConnection: false, maxAuthAttempts: 3, bannedCiphers: ['des', '3des', 'rc4'], requiredServerHostKeyAlgs: ['ssh-rsa', 'ssh-ed25519'] }, performance: { maxConcurrentTransfers: 5, transferTimeout: 300000, // 5 minutes chunkSize: 65536, // 64KB for high-speed networks windowSize: 2097152, // 2MB packetSize: 32768, compression: 'zlib@openssh.com' }, retry: { maxAttempts: 3, backoffMultiplier: 2, initialDelay: 1000, maxDelay: 30000 } }; ``` ### Multi-Server Configuration ```typescript const multiServerConfig = { servers: { production: { host: 'prod-sftp.example.com', port: 22, username: 'produser', privateKey: '/keys/prod_key', basePath: '/var/www/production' }, staging: { host: 'staging-sftp.example.com', port: 22, username: 'stageuser', privateKey: '/keys/staging_key', basePath: '/var/www/staging' }, development: { host: 'dev-sftp.example.com', port: 22, username: 'devuser', password: 'devpass123', basePath: '/var/www/development' } }, defaultServer: 'development' }; ``` ## Usage Examples ### 1. Basic File Operations ```javascript // Connect to SFTP server const sftpSession = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: 'files.example.com', port: 22, username: 'fileuser', password: 'secure123' } }); // Upload a file await console_execute_command({ command: 'sftp-put', args: ['local-file.txt', 'remote-file.txt'], consoleType: 'sftp', sessionId: sftpSession.sessionId }); // Download a file await console_execute_command({ command: 'sftp-get', args: ['remote-file.txt', 'downloaded-file.txt'], consoleType: 'sftp', sessionId: sftpSession.sessionId }); // List remote directory const listResult = await console_execute_command({ command: 'sftp-ls', args: ['/home/user/documents'], consoleType: 'sftp', sessionId: sftpSession.sessionId }); console.log('Remote files:', listResult.output); // Create directory await console_execute_command({ command: 'sftp-mkdir', args: ['/home/user/backup'], consoleType: 'sftp', sessionId: sftpSession.sessionId }); ``` ### 2. Web Application Deployment ```javascript // Deploy web application to production server async function deployApplication(version) { const deploySession = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: 'web-server.example.com', port: 22, username: 'webdeploy', privateKey: '/keys/deploy_key', basePath: '/var/www/html' } }); try { // Create backup of current version const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); await console_execute_command({ command: 'sftp-mkdir', args: [`backups/backup-${timestamp}`], consoleType: 'sftp', sessionId: deploySession.sessionId }); // Backup current files await console_execute_command({ command: 'sftp-get-recursive', args: ['current/', `backups/backup-${timestamp}/`], consoleType: 'sftp', sessionId: deploySession.sessionId }); // Upload new version console.log('Uploading new application version...'); const uploadResult = await console_execute_command({ command: 'sftp-put-recursive', args: [`builds/${version}/`, 'staging/'], consoleType: 'sftp', sessionId: deploySession.sessionId, options: { preserveTimestamps: true, overwrite: true, excludePatterns: ['*.log', '*.tmp', 'node_modules/'] } }); if (uploadResult.exitCode === 0) { console.log('Upload successful, activating new version...'); // Atomic swap - rename directories await console_execute_command({ command: 'sftp-rename', args: ['current', `old-${timestamp}`], consoleType: 'sftp', sessionId: deploySession.sessionId }); await console_execute_command({ command: 'sftp-rename', args: ['staging', 'current'], consoleType: 'sftp', sessionId: deploySession.sessionId }); console.log(`Deployment of version ${version} completed successfully`); } else { throw new Error('Upload failed'); } } catch (error) { console.error('Deployment failed:', error.message); // Rollback if necessary console.log('Initiating rollback...'); await rollbackDeployment(deploySession.sessionId, timestamp); throw error; } finally { await console_stop_session({ sessionId: deploySession.sessionId }); } } // Monitor deployment progress protocol.on('transfer-progress', (progress, session) => { const percent = ((progress.transferred / progress.total) * 100).toFixed(1); console.log(`Transfer progress: ${percent}% (${progress.filename})`); }); protocol.on('transfer-complete', (result, session) => { console.log(`File transfer completed: ${result.filename} (${result.size} bytes)`); }); ``` ### 3. Automated Backup System ```javascript // Automated backup system async function performBackup() { const backupSession = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: 'backup-server.example.com', port: 22, username: 'backupuser', privateKey: '/keys/backup_key' } }); const timestamp = new Date().toISOString().split('T')[0]; // YYYY-MM-DD const backupPath = `/backups/${timestamp}`; try { // Create backup directory await console_execute_command({ command: 'sftp-mkdir-recursive', args: [backupPath], consoleType: 'sftp', sessionId: backupSession.sessionId }); // Define backup sets const backupSets = [ { name: 'databases', source: '/var/backups/databases', destination: `${backupPath}/databases`, compress: true }, { name: 'configurations', source: '/etc', destination: `${backupPath}/configurations`, excludePatterns: ['*.log', 'cache/*', 'tmp/*'] }, { name: 'user-data', source: '/home', destination: `${backupPath}/user-data`, includePatterns: ['*.doc*', '*.pdf', '*.jpg', '*.png'] } ]; // Perform backups for (const backupSet of backupSets) { console.log(`Starting backup: ${backupSet.name}`); const backupResult = await console_execute_command({ command: 'sftp-put-recursive', args: [backupSet.source, backupSet.destination], consoleType: 'sftp', sessionId: backupSession.sessionId, options: { compress: backupSet.compress || false, excludePatterns: backupSet.excludePatterns || [], includePatterns: backupSet.includePatterns || [], preserveTimestamps: true, checksumVerification: true } }); if (backupResult.exitCode === 0) { console.log(`Backup completed: ${backupSet.name}`); } else { console.error(`Backup failed: ${backupSet.name} - ${backupResult.stderr}`); } } // Generate backup manifest const manifest = { timestamp: new Date().toISOString(), backupSets: backupSets, totalSize: await getTotalBackupSize(backupSession.sessionId, backupPath), checksums: await generateChecksums(backupSession.sessionId, backupPath) }; // Upload manifest const manifestContent = JSON.stringify(manifest, null, 2); await console_execute_command({ command: 'sftp-put-content', args: [manifestContent, `${backupPath}/manifest.json`], consoleType: 'sftp', sessionId: backupSession.sessionId }); console.log(`Backup completed successfully: ${backupPath}`); // Cleanup old backups (keep last 7 days) await cleanupOldBackups(backupSession.sessionId, 7); } catch (error) { console.error('Backup failed:', error.message); // Send alert notification await sendBackupAlert('FAILED', error.message); } finally { await console_stop_session({ sessionId: backupSession.sessionId }); } } // Run backup daily at 2 AM const backupSchedule = require('node-cron'); backupSchedule.schedule('0 2 * * *', performBackup); ``` ### 4. Log File Management ```javascript // Collect and archive log files from multiple servers async function collectLogs(dateRange) { const servers = [ { name: 'web-server-1', host: 'web1.example.com' }, { name: 'web-server-2', host: 'web2.example.com' }, { name: 'api-server', host: 'api.example.com' }, { name: 'database-server', host: 'db.example.com' } ]; const logCollectionPath = `/logs/collection-${Date.now()}`; require('fs').mkdirSync(logCollectionPath, { recursive: true }); for (const server of servers) { console.log(`Collecting logs from ${server.name}...`); try { const serverSession = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: server.host, port: 22, username: 'logcollector', privateKey: '/keys/log_collector_key' } }); // List available log files const logListResult = await console_execute_command({ command: 'sftp-ls', args: ['/var/log', '--pattern=*.log', '--pattern=*.log.*'], consoleType: 'sftp', sessionId: serverSession.sessionId }); const logFiles = parseFileList(logListResult.output); const filteredLogs = logFiles.filter(file => isWithinDateRange(file.modifiedDate, dateRange.start, dateRange.end) ); // Download log files for (const logFile of filteredLogs) { const localPath = `${logCollectionPath}/${server.name}_${logFile.name}`; await console_execute_command({ command: 'sftp-get', args: [logFile.path, localPath], consoleType: 'sftp', sessionId: serverSession.sessionId, options: { compress: true, // Compress during transfer resume: true // Resume if interrupted } }); console.log(`Downloaded: ${logFile.name} from ${server.name}`); } await console_stop_session({ sessionId: serverSession.sessionId }); } catch (error) { console.error(`Failed to collect logs from ${server.name}:`, error.message); } } // Compress collected logs const archivePath = `${logCollectionPath}.tar.gz`; await console_execute_command({ command: 'tar', args: ['-czf', archivePath, logCollectionPath] }); console.log(`Log collection completed: ${archivePath}`); // Upload to central log storage await uploadToLogStorage(archivePath); // Cleanup temporary files require('fs').rmSync(logCollectionPath, { recursive: true }); } ``` ### 5. Configuration Management ```javascript // Synchronize configuration files across servers async function syncConfigurations(environment) { const configMaster = { host: 'config-master.example.com', path: `/configs/${environment}` }; const targetServers = [ { name: 'web-1', host: 'web1.example.com', services: ['nginx', 'php-fpm'] }, { name: 'web-2', host: 'web2.example.com', services: ['nginx', 'php-fpm'] }, { name: 'api', host: 'api.example.com', services: ['api-server', 'redis'] }, { name: 'db', host: 'db.example.com', services: ['mysql', 'backup'] } ]; // Connect to configuration master const masterSession = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: configMaster.host, port: 22, username: 'configadmin', privateKey: '/keys/config_key' } }); // Get configuration manifest const manifestResult = await console_execute_command({ command: 'sftp-get-content', args: [`${configMaster.path}/manifest.json`], consoleType: 'sftp', sessionId: masterSession.sessionId }); const configManifest = JSON.parse(manifestResult.output); // Sync configurations to each server for (const server of targetServers) { console.log(`Syncing configurations to ${server.name}...`); try { const serverSession = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: server.host, port: 22, username: 'configsync', privateKey: '/keys/sync_key' } }); // Download current configurations for backup const backupPath = `/tmp/config-backup-${Date.now()}`; await console_execute_command({ command: 'sftp-get-recursive', args: ['/etc/services/', backupPath], consoleType: 'sftp', sessionId: serverSession.sessionId }); // Sync relevant configurations for (const service of server.services) { if (configManifest.services[service]) { const serviceConfig = configManifest.services[service]; for (const configFile of serviceConfig.files) { // Download from master const tempFile = `/tmp/${configFile.name}`; await console_execute_command({ command: 'sftp-get', args: [`${configMaster.path}/${service}/${configFile.path}`, tempFile], consoleType: 'sftp', sessionId: masterSession.sessionId }); // Upload to target server await console_execute_command({ command: 'sftp-put', args: [tempFile, configFile.targetPath], consoleType: 'sftp', sessionId: serverSession.sessionId, options: { preservePermissions: true, backup: true } }); // Verify checksum const checksumResult = await console_execute_command({ command: 'sftp-checksum', args: [configFile.targetPath, 'sha256'], consoleType: 'sftp', sessionId: serverSession.sessionId }); if (checksumResult.output.trim() === configFile.checksum) { console.log(`✓ ${service}/${configFile.name} synced successfully`); } else { console.error(`✗ ${service}/${configFile.name} checksum mismatch`); } // Cleanup temp file require('fs').unlinkSync(tempFile); } // Restart service if needed if (serviceConfig.restartRequired) { console.log(`Restarting ${service} service...`); // This would be done via SSH, not SFTP // await restartService(server.host, service); } } } await console_stop_session({ sessionId: serverSession.sessionId }); console.log(`Configuration sync completed for ${server.name}`); } catch (error) { console.error(`Configuration sync failed for ${server.name}:`, error.message); } } await console_stop_session({ sessionId: masterSession.sessionId }); } ``` ### 6. File Monitoring and Synchronization ```javascript // Monitor and sync files between local and remote locations class SFTPFileSyncer { constructor(localPath, remotePath, sftpConfig) { this.localPath = localPath; this.remotePath = remotePath; this.sftpConfig = sftpConfig; this.session = null; this.watchedFiles = new Map(); this.syncInterval = 30000; // 30 seconds } async initialize() { // Connect to SFTP this.session = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: this.sftpConfig }); // Start file monitoring this.startFileMonitoring(); // Start periodic sync this.startPeriodicSync(); console.log('File syncer initialized'); } startFileMonitoring() { const fs = require('fs'); const chokidar = require('chokidar'); const watcher = chokidar.watch(this.localPath, { ignored: /(^|[\/\\])\../, // ignore dotfiles persistent: true, ignoreInitial: false }); watcher.on('change', async (filePath) => { console.log(`File changed: ${filePath}`); await this.syncFile(filePath, 'upload'); }); watcher.on('add', async (filePath) => { console.log(`File added: ${filePath}`); await this.syncFile(filePath, 'upload'); }); watcher.on('unlink', async (filePath) => { console.log(`File removed: ${filePath}`); await this.syncFile(filePath, 'delete'); }); } async syncFile(localFilePath, action) { try { const relativePath = path.relative(this.localPath, localFilePath); const remoteFilePath = path.posix.join(this.remotePath, relativePath); switch (action) { case 'upload': await console_execute_command({ command: 'sftp-put', args: [localFilePath, remoteFilePath], consoleType: 'sftp', sessionId: this.session.sessionId, options: { createDirectories: true, preserveTimestamps: true } }); console.log(`Uploaded: ${relativePath}`); break; case 'delete': await console_execute_command({ command: 'sftp-rm', args: [remoteFilePath], consoleType: 'sftp', sessionId: this.session.sessionId }); console.log(`Deleted remote: ${relativePath}`); break; } } catch (error) { console.error(`Sync failed for ${localFilePath}:`, error.message); } } startPeriodicSync() { setInterval(async () => { await this.performBidirectionalSync(); }, this.syncInterval); } async performBidirectionalSync() { try { // Get local file list const localFiles = await this.getLocalFileList(); // Get remote file list const remoteListResult = await console_execute_command({ command: 'sftp-ls-recursive', args: [this.remotePath], consoleType: 'sftp', sessionId: this.session.sessionId }); const remoteFiles = this.parseRemoteFileList(remoteListResult.output); // Compare and sync differences await this.syncDifferences(localFiles, remoteFiles); } catch (error) { console.error('Periodic sync failed:', error.message); } } async syncDifferences(localFiles, remoteFiles) { const localFileMap = new Map(localFiles.map(f => [f.relativePath, f])); const remoteFileMap = new Map(remoteFiles.map(f => [f.relativePath, f])); // Files that exist locally but not remotely (upload) for (const [relativePath, localFile] of localFileMap) { if (!remoteFileMap.has(relativePath)) { await this.syncFile(localFile.fullPath, 'upload'); } else { // Check if local file is newer const remoteFile = remoteFileMap.get(relativePath); if (localFile.mtime > remoteFile.mtime) { await this.syncFile(localFile.fullPath, 'upload'); } } } // Files that exist remotely but not locally (download) for (const [relativePath, remoteFile] of remoteFileMap) { if (!localFileMap.has(relativePath)) { await this.downloadFile(remoteFile); } else { // Check if remote file is newer const localFile = localFileMap.get(relativePath); if (remoteFile.mtime > localFile.mtime) { await this.downloadFile(remoteFile); } } } } async downloadFile(remoteFile) { const localFilePath = path.join(this.localPath, remoteFile.relativePath); const localDir = path.dirname(localFilePath); // Create local directory if it doesn't exist require('fs').mkdirSync(localDir, { recursive: true }); await console_execute_command({ command: 'sftp-get', args: [remoteFile.fullPath, localFilePath], consoleType: 'sftp', sessionId: this.session.sessionId, options: { preserveTimestamps: true } }); console.log(`Downloaded: ${remoteFile.relativePath}`); } } // Usage const syncer = new SFTPFileSyncer( '/local/project/files', '/remote/backup/files', { host: 'sync-server.example.com', username: 'syncuser', privateKey: '/keys/sync_key' } ); await syncer.initialize(); ``` ## Advanced Features ### Resume Interrupted Transfers ```javascript // Resume large file transfers async function resumableUpload(localFile, remoteFile, sessionId) { try { // Check if partial file exists on remote const remoteStatResult = await console_execute_command({ command: 'sftp-stat', args: [remoteFile], consoleType: 'sftp', sessionId: sessionId }); const localStats = require('fs').statSync(localFile); let resumeFrom = 0; if (remoteStatResult.exitCode === 0) { const remoteStat = JSON.parse(remoteStatResult.output); resumeFrom = remoteStat.size; if (resumeFrom >= localStats.size) { console.log('File already fully transferred'); return; } } // Upload with resume await console_execute_command({ command: 'sftp-put', args: [localFile, remoteFile], consoleType: 'sftp', sessionId: sessionId, options: { resume: true, resumeFrom: resumeFrom } }); console.log(`Upload completed (resumed from ${resumeFrom} bytes)`); } catch (error) { console.error('Resumable upload failed:', error.message); throw error; } } ``` ### Batch Operations with Progress ```javascript // Upload multiple files with progress tracking async function batchUpload(fileList, remotePath, sessionId) { const totalFiles = fileList.length; let completedFiles = 0; let totalBytes = 0; let transferredBytes = 0; // Calculate total size for (const file of fileList) { const stats = require('fs').statSync(file.localPath); totalBytes += stats.size; } console.log(`Starting batch upload: ${totalFiles} files (${formatBytes(totalBytes)})`); // Track progress protocol.on('transfer-progress', (progress, session) => { if (session.sessionId === sessionId) { const overallProgress = ((transferredBytes + progress.transferred) / totalBytes * 100).toFixed(1); process.stdout.write(`\rProgress: ${overallProgress}% - ${progress.filename}`); } }); protocol.on('transfer-complete', (result, session) => { if (session.sessionId === sessionId) { completedFiles++; transferredBytes += result.size; const overallProgress = (transferredBytes / totalBytes * 100).toFixed(1); console.log(`\n[${completedFiles}/${totalFiles}] ${result.filename} - ${overallProgress}% complete`); } }); // Upload files with controlled concurrency const concurrency = 3; const batches = []; for (let i = 0; i < fileList.length; i += concurrency) { batches.push(fileList.slice(i, i + concurrency)); } for (const batch of batches) { const uploadPromises = batch.map(async (file) => { const remoteFile = path.posix.join(remotePath, file.relativePath); return console_execute_command({ command: 'sftp-put', args: [file.localPath, remoteFile], consoleType: 'sftp', sessionId: sessionId, options: { createDirectories: true, preserveTimestamps: true, checksumVerification: true } }); }); await Promise.allSettled(uploadPromises); } console.log(`\nBatch upload completed: ${completedFiles}/${totalFiles} files`); } function formatBytes(bytes) { const sizes = ['Bytes', 'KB', 'MB', 'GB']; if (bytes === 0) return '0 Bytes'; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; } ``` ### File Integrity Verification ```javascript // Verify file integrity after transfer async function verifyFileIntegrity(localFile, remoteFile, sessionId, algorithm = 'sha256') { // Calculate local file checksum const localChecksum = await calculateLocalChecksum(localFile, algorithm); // Get remote file checksum const remoteChecksumResult = await console_execute_command({ command: 'sftp-checksum', args: [remoteFile, algorithm], consoleType: 'sftp', sessionId: sessionId }); if (remoteChecksumResult.exitCode !== 0) { throw new Error('Failed to calculate remote checksum'); } const remoteChecksum = remoteChecksumResult.output.trim(); if (localChecksum === remoteChecksum) { console.log(`✓ File integrity verified: ${path.basename(localFile)}`); return true; } else { console.error(`✗ File integrity check failed: ${path.basename(localFile)}`); console.error(`Local: ${localChecksum}`); console.error(`Remote: ${remoteChecksum}`); return false; } } async function calculateLocalChecksum(filePath, algorithm) { const crypto = require('crypto'); const fs = require('fs'); return new Promise((resolve, reject) => { const hash = crypto.createHash(algorithm); const stream = fs.createReadStream(filePath); stream.on('data', data => hash.update(data)); stream.on('end', () => resolve(hash.digest('hex'))); stream.on('error', reject); }); } ``` ## Error Handling ### Connection and Authentication Errors ```javascript protocol.on('connection-error', (error, config) => { console.error(`SFTP connection failed to ${config.host}:${config.port}:`, error.message); if (error.level === 'client-authentication') { console.error('Authentication failed - check credentials'); } else if (error.code === 'ENOTFOUND') { console.error('Host not found - check hostname and network connectivity'); } else if (error.code === 'ECONNREFUSED') { console.error('Connection refused - check if SSH service is running'); } else if (error.code === 'ETIMEDOUT') { console.error('Connection timeout - check network and firewall settings'); } }); protocol.on('authentication-error', (error, config) => { console.error(`Authentication failed for user ${config.username}@${config.host}`); if (config.privateKey) { console.log('Check private key path and passphrase'); } else { console.log('Check username and password'); } }); ``` ### Transfer Errors ```javascript protocol.on('transfer-error', async (error, operation, session) => { console.error(`Transfer error during ${operation.type}:`, error.message); if (error.code === 'ENOSPC') { console.error('No space left on device'); } else if (error.code === 'EACCES') { console.error('Permission denied'); } else if (error.code === 'EISDIR') { console.error('Target is a directory, expected file'); } else if (error.message.includes('No such file')) { console.error('Source file not found'); // Auto-retry after short delay setTimeout(async () => { try { await retryOperation(operation, session); } catch (retryError) { console.error('Retry failed:', retryError.message); } }, 5000); } }); async function retryOperation(operation, session, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { console.log(`Retry attempt ${attempt}/${maxRetries} for ${operation.type}`); await console_execute_command({ command: operation.command, args: operation.args, consoleType: 'sftp', sessionId: session.sessionId, options: operation.options }); console.log('Retry successful'); return; } catch (error) { console.error(`Retry attempt ${attempt} failed:`, error.message); if (attempt === maxRetries) { throw error; } // Exponential backoff await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); } } } ``` ## Best Practices ### 1. Security ```javascript // Use key-based authentication const secureConfig = { host: 'secure-server.com', username: 'deployuser', privateKey: require('fs').readFileSync('/secure/path/to/private/key'), passphrase: process.env.SSH_KEY_PASSPHRASE, // Store in environment algorithms: { kex: ['curve25519-sha256', 'diffie-hellman-group16-sha512'], cipher: ['chacha20-poly1305@openssh.com', 'aes256-gcm'], serverHostKey: ['ssh-ed25519', 'ecdsa-sha2-nistp256'], hmac: ['umac-128-etm@openssh.com', 'hmac-sha2-256-etm@openssh.com'] }, hostVerification: true, strictHostKeyChecking: true }; // Implement connection timeout const connectionOptions = { readyTimeout: 30000, keepAliveInterval: 60000, keepAliveCountMax: 3 }; // Use secure file permissions const transferOptions = { preservePermissions: true, mode: 0o600, // Read/write for owner only uid: 1000, // Set appropriate user gid: 1000 // Set appropriate group }; ``` ### 2. Performance ```javascript // Optimize for your network conditions const performanceConfig = { transfer: { concurrency: 5, // Adjust based on bandwidth chunkSize: 65536, // 64KB for high-speed networks windowSize: 2097152, // 2MB compression: true, // Enable for slow connections packetSize: 32768 }, retry: { maxAttempts: 3, backoffMultiplier: 1.5, initialDelay: 1000, maxDelay: 30000 } }; // Use connection pooling for multiple operations class SFTPConnectionPool { constructor(config, maxConnections = 5) { this.config = config; this.maxConnections = maxConnections; this.availableConnections = []; this.busyConnections = new Set(); } async getConnection() { if (this.availableConnections.length > 0) { const connection = this.availableConnections.pop(); this.busyConnections.add(connection); return connection; } if (this.busyConnections.size < this.maxConnections) { const connection = await this.createConnection(); this.busyConnections.add(connection); return connection; } // Wait for connection to become available return new Promise((resolve) => { const checkForConnection = () => { if (this.availableConnections.length > 0) { const connection = this.availableConnections.pop(); this.busyConnections.add(connection); resolve(connection); } else { setTimeout(checkForConnection, 100); } }; checkForConnection(); }); } releaseConnection(connection) { this.busyConnections.delete(connection); this.availableConnections.push(connection); } } ``` ### 3. Monitoring and Logging ```javascript // Comprehensive logging const logger = require('winston').createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'sftp-error.log', level: 'error' }), new winston.transports.File({ filename: 'sftp-combined.log' }), new winston.transports.Console({ format: winston.format.simple() }) ] }); // Monitor transfer statistics protocol.on('transfer-complete', (result, session) => { logger.info('Transfer completed', { filename: result.filename, size: result.size, duration: result.duration, speed: result.speed, sessionId: session.sessionId, remoteHost: session.config.host }); }); // Track connection health protocol.on('connection-established', (config) => { logger.info('SFTP connection established', { host: config.host, username: config.username, timestamp: new Date().toISOString() }); }); ``` ## Troubleshooting ### Common Issues #### 1. Permission Denied ```javascript // Check file permissions const statResult = await console_execute_command({ command: 'sftp-stat', args: ['/path/to/file'], consoleType: 'sftp', sessionId: sessionId }); console.log('File permissions:', JSON.parse(statResult.output)); // Fix permissions if needed await console_execute_command({ command: 'sftp-chmod', args: ['644', '/path/to/file'], consoleType: 'sftp', sessionId: sessionId }); ``` #### 2. Connection Timeouts ```javascript // Increase timeout values const robustConfig = { readyTimeout: 60000, // 1 minute keepAliveInterval: 30000, // 30 seconds keepAliveCountMax: 5, connectionTimeout: 120000 // 2 minutes }; // Implement connection retry logic async function connectWithRetry(config, maxAttempts = 3) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: config }); } catch (error) { console.log(`Connection attempt ${attempt}/${maxAttempts} failed:`, error.message); if (attempt === maxAttempts) throw error; await new Promise(resolve => setTimeout(resolve, attempt * 2000)); } } } ``` #### 3. Host Key Verification ```javascript // Handle host key verification protocol.on('host-key-verification', async (hostKey, config) => { console.log('Host key verification required'); console.log('Host:', config.host); console.log('Key fingerprint:', hostKey.fingerprint); // Auto-accept for known safe hosts (be careful!) const trustedHosts = ['internal-server.company.com']; if (trustedHosts.includes(config.host)) { return true; // Accept } // Prompt user or check against known_hosts const knownHosts = require('fs').readFileSync('/home/user/.ssh/known_hosts', 'utf8'); return knownHosts.includes(hostKey.fingerprint); }); ``` ## Migration Guide ### From FTP to SFTP #### Before (FTP) ```javascript const ftp = require('ftp'); const client = new ftp(); client.connect({ host: 'ftp.example.com', user: 'username', password: 'password' }); client.put('local-file.txt', 'remote-file.txt', (err) => { if (err) throw err; console.log('Upload complete'); client.end(); }); ``` #### After (SFTP via MCP) ```javascript const session = await console_create_session({ command: 'sftp-connect', consoleType: 'sftp', sftpOptions: { host: 'sftp.example.com', // Changed from FTP to SFTP port: 22, // Changed from 21 to 22 username: 'username', password: 'password' // Consider using private key instead } }); await console_execute_command({ command: 'sftp-put', args: ['local-file.txt', 'remote-file.txt'], consoleType: 'sftp', sessionId: session.sessionId }); console.log('Secure upload complete'); ``` ## API Reference ### Events - `connection-established`: Connection successful - `connection-error`: Connection failed - `authentication-error`: Authentication failed - `transfer-progress`: Transfer progress update - `transfer-complete`: Transfer completed - `transfer-error`: Transfer failed - `host-key-verification`: Host key verification needed ### Methods - `createSession(options)`: Create SFTP session - `uploadFile(local, remote, options)`: Upload single file - `downloadFile(remote, local, options)`: Download single file - `uploadDirectory(localDir, remoteDir, options)`: Upload directory - `downloadDirectory(remoteDir, localDir, options)`: Download directory - `listFiles(remotePath, options)`: List remote files - `createDirectory(remotePath)`: Create remote directory - `removeFile(remotePath)`: Remove remote file - `removeDirectory(remotePath)`: Remove remote directory - `renameFile(oldPath, newPath)`: Rename remote file - `setFilePermissions(remotePath, mode)`: Set file permissions - `getFileStats(remotePath)`: Get file statistics - `calculateChecksum(remotePath, algorithm)`: Calculate file checksum ### Configuration Options See the TypeScript interfaces in the source code for complete configuration options including `SFTPProtocolConfig`, `SFTPConnectionOptions`, and `SFTPTransferOptions`.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ooples/mcp-console-automation'

If you have feedback or need assistance with the MCP directory API, please join our Discord server