Skip to main content
Glama
fkom13

MCP SFTP Orchestrator

by fkom13

task_transfer_multi

Execute multiple SFTP file transfers using glob patterns (*, ?, []) for batch uploads or downloads between local and remote systems.

Instructions

Lance des transferts SFTP multiples avec support de patterns glob (*, ?, []).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
aliasYesAlias du serveur cible.
directionYes
filesYesListe des fichiers à transférer
rappelNoDéfinit un rappel en secondes.

Implementation Reference

  • The inline handler function for the 'task_transfer_multi' tool. It creates a job with the files array, logs it, triggers the SFTP multi-transfer execution, awaits completion or timeout, and returns the result or background notification.
    async (params) => {
        const job = queue.addJob({ 
            type: 'sftp', 
            ...params, 
            status: 'pending',
            files: params.files
        });
        history.logTask(job);
        sftp.executeMultiTransfer(job.id);
    
        const finalJob = await waitForJobCompletion(job.id, config.syncTimeout);
        if (finalJob) {
            return { content: [{ type: "text", text: `Résultat transferts multiples (tâche ${finalJob.id}):\n${JSON.stringify(finalJob, null, 2)}` }] };
        } else {
            return { content: [{ type: "text", text: `Tâche de transferts multiples ${job.id} initiée en arrière-plan.` }] };
        }
    }
  • Input schema definition for the task_transfer_multi tool, including parameters for server alias, direction, list of local/remote file pairs supporting globs, and optional reminder.
    {
        title: "Transférer plusieurs fichiers/dossiers (SFTP)",
        description: "Lance des transferts SFTP multiples avec support de patterns glob (*, ?, []).",
        inputSchema: z.object({
            alias: z.string().describe("Alias du serveur cible."),
            direction: z.enum(['upload', 'download']),
            files: z.array(z.object({
                local: z.string().describe("Chemin local ou pattern glob (ex: /home/*.txt)"),
                remote: z.string().describe("Chemin distant")
            })).describe("Liste des fichiers à transférer"),
            rappel: z.number().optional().describe("Définit un rappel en secondes.")
        })
    },
  • server.js:511-543 (registration)
    Full registration of the task_transfer_multi tool on the MCP server, including name, schema, and handler function.
    server.registerTool(
        "task_transfer_multi",
        {
            title: "Transférer plusieurs fichiers/dossiers (SFTP)",
            description: "Lance des transferts SFTP multiples avec support de patterns glob (*, ?, []).",
            inputSchema: z.object({
                alias: z.string().describe("Alias du serveur cible."),
                direction: z.enum(['upload', 'download']),
                files: z.array(z.object({
                    local: z.string().describe("Chemin local ou pattern glob (ex: /home/*.txt)"),
                    remote: z.string().describe("Chemin distant")
                })).describe("Liste des fichiers à transférer"),
                rappel: z.number().optional().describe("Définit un rappel en secondes.")
            })
        },
        async (params) => {
            const job = queue.addJob({ 
                type: 'sftp', 
                ...params, 
                status: 'pending',
                files: params.files
            });
            history.logTask(job);
            sftp.executeMultiTransfer(job.id);
    
            const finalJob = await waitForJobCompletion(job.id, config.syncTimeout);
            if (finalJob) {
                return { content: [{ type: "text", text: `Résultat transferts multiples (tâche ${finalJob.id}):\n${JSON.stringify(finalJob, null, 2)}` }] };
            } else {
                return { content: [{ type: "text", text: `Tâche de transferts multiples ${job.id} initiée en arrière-plan.` }] };
            }
        }
    );
  • Core helper function executeTransfer that performs the actual SFTP operations. Handles multi-file transfers by looping over job.files, supports glob expansion, upload/download of files and directories, connection management, progress tracking, error handling, and job status updates.
    async function executeTransfer(jobId) {
        const job = queue.getJob(jobId);
        if (!job) return queue.log('error', `Tâche introuvable: ${jobId}`);
    
        let sftp = null;
        try {
            const serverConfig = await serverManager.getServer(job.alias);
            queue.updateJobStatus(jobId, 'running');
    
            sftp = new SftpClient();
    
            // Configuration de la connexion
            const config = {
                host: serverConfig.host,
                port: 22,
                username: serverConfig.user,
                readyTimeout: 20000,
                retries: 3,
                retry_factor: 2,
                retry_minTimeout: 2000
            };
            
            if (serverConfig.keyPath) {
                config.privateKey = await fs.readFile(serverConfig.keyPath);
            } else if (serverConfig.password) {
                config.password = serverConfig.password;
            } else {
                throw new Error(`Aucune méthode d'authentification pour '${job.alias}'.`);
            }
    
            await sftp.connect(config);
            
            // Déterminer si on traite plusieurs fichiers
            const files = job.files || [{ local: job.local, remote: job.remote }];
            const isMultiple = Array.isArray(job.files) && job.files.length > 1;
            
            let successCount = 0;
            let failedFiles = [];
            let totalFiles = 0;
            
            // Traiter chaque fichier
            for (let i = 0; i < files.length; i++) {
                const file = files[i];
                const progress = isMultiple ? ` (${i + 1}/${files.length})` : '';
                
                try {
                    if (job.direction === 'upload') {
                        const localFiles = await expandFileList(file.local);
                        totalFiles += localFiles.length;
                        for (const localFile of localFiles) {
                            queue.log('info', `Transfert${progress}: ${localFile}`);
                            await handleUpload(sftp, localFile, file.remote);
                            successCount++;
                        }
                    } else if (job.direction === 'download') {
                        const downloadedCount = await handleDownload(sftp, file.remote, file.local);
                        totalFiles += downloadedCount;
                        successCount += downloadedCount;
                    }
                } catch (err) {
                    queue.log('error', `Échec transfert ${file.local || file.remote}: ${err.message}`);
                    failedFiles.push({ file: file.local || file.remote, error: err.message });
                }
            }
            
            await sftp.end();
            
            // Génération du rapport
            let status = successCount === totalFiles ? 'completed' : 'partial';
            let output = `Transfert ${job.direction}: ${successCount}/${totalFiles} fichiers réussis`;
            
            if (failedFiles.length > 0) {
                output += `\nÉchecs: ${failedFiles.map(f => f.file).join(', ')}`;
                if (successCount === 0) status = 'failed';
            }
            
            queue.updateJobStatus(jobId, status, { output, failedFiles });
            
        } catch (err) {
            queue.updateJobStatus(jobId, 'failed', { error: err.message });
        } finally {
            if (sftp) {
                try {
                    await sftp.end();
                } catch (e) {
                    // Ignorer les erreurs de fermeture
                }
            }
        }
    }
  • Thin wrapper executeMultiTransfer that retrieves the job and delegates to the core executeTransfer function.
    async function executeMultiTransfer(jobId) {
        const job = queue.getJob(jobId);
        if (!job) return queue.log('error', `Tâche introuvable: ${jobId}`);
        
        // Utilise la même fonction mais avec support multi-fichiers
        return executeTransfer(jobId);
    }

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/fkom13/mcp-sftp-orchestrator'

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