import { MCPTool } from "mcp-framework";
import axios from "axios";
import fs from "fs";
import path from "path";
import { z } from "zod";
interface WorkflowManagerInput {
action: "list" | "get" | "create" | "update" | "delete" | "export" | "import";
workflowId?: string;
workflowPath?: string;
workflowData?: any;
tags?: string[];
}
class WorkflowManagerTool extends MCPTool<WorkflowManagerInput> {
private apiUrl = process.env.N8N_API_URL || "http://localhost:5678/api/v1";
private apiKey = process.env.N8N_API_KEY || "";
name = "workflow_manager";
description = "Gère les workflows n8n (liste, récupère, crée, met à jour, supprime, exporte, importe)";
schema = {
action: {
type: z.enum(["list", "get", "create", "update", "delete", "export", "import"]),
description: "Action à effectuer sur les workflows"
},
workflowId: {
type: z.string().optional(),
description: "ID du workflow (requis pour get, update, delete, export)"
},
workflowPath: {
type: z.string().optional(),
description: "Chemin du fichier de workflow (requis pour export, import)"
},
workflowData: {
type: z.any().optional(),
description: "Données du workflow (requis pour create, update)"
},
tags: {
type: z.array(z.string()).optional(),
description: "Tags pour filtrer les workflows (optionnel pour list)"
}
};
private getAxiosConfig() {
return {
headers: {
"X-N8N-API-KEY": this.apiKey,
"Content-Type": "application/json",
},
};
}
private logApiCall(method: string, url: string, data?: any) {
console.log(`[WorkflowManagerTool] Appel API: ${method} ${url}`);
if (data) {
console.log(`[WorkflowManagerTool] Données: ${JSON.stringify(data).substring(0, 200)}...`);
}
}
private handleApiError(error: any, action: string) {
console.error(`[WorkflowManagerTool] Erreur lors de l'action '${action}':`, error);
if (error.response) {
console.error(`[WorkflowManagerTool] Statut: ${error.response.status}`);
console.error(`[WorkflowManagerTool] Données: ${JSON.stringify(error.response.data)}`);
} else if (error.request) {
console.error('[WorkflowManagerTool] Aucune réponse reçue du serveur');
console.error(error.request);
} else {
console.error(`[WorkflowManagerTool] Erreur: ${error.message}`);
}
return {
success: false,
error: error.message,
details: error.response?.data || "Erreur inconnue",
};
}
async execute(options: WorkflowManagerInput): Promise<any> {
const { action, workflowId, workflowPath, workflowData, tags } = options;
try {
console.log(`[WorkflowManagerTool] Exécution de l'action: ${action}`);
switch (action) {
case "list":
return this.listWorkflows(tags);
case "get":
if (!workflowId) throw new Error("workflowId est requis pour l'action get");
return this.getWorkflow(workflowId);
case "create":
if (!workflowData) throw new Error("workflowData est requis pour l'action create");
return this.createWorkflow(workflowData);
case "update":
if (!workflowId) throw new Error("workflowId est requis pour l'action update");
if (!workflowData) throw new Error("workflowData est requis pour l'action update");
return this.updateWorkflow(workflowId, workflowData);
case "delete":
if (!workflowId) throw new Error("workflowId est requis pour l'action delete");
return this.deleteWorkflow(workflowId);
case "export":
if (!workflowId) throw new Error("workflowId est requis pour l'action export");
if (!workflowPath) throw new Error("workflowPath est requis pour l'action export");
return this.exportWorkflow(workflowId, workflowPath);
case "import":
if (!workflowPath) throw new Error("workflowPath est requis pour l'action import");
return this.importWorkflow(workflowPath);
default:
throw new Error(`Action non reconnue: ${action}`);
}
} catch (error: any) {
console.error(`[WorkflowManagerTool] Erreur lors de l'exécution de l'action '${action}':`, error);
return {
success: false,
error: error.message,
details: "Erreur lors de l'exécution de l'action",
};
}
}
private async listWorkflows(tags?: string[]) {
try {
const url = `${this.apiUrl}/workflows`;
this.logApiCall('GET', url);
const response = await axios.get(url, this.getAxiosConfig());
console.log(`[WorkflowManagerTool] ${response.data.data.length} workflows récupérés`);
let workflows = response.data.data;
// Filtrer par tags si spécifié
if (tags && tags.length > 0) {
console.log(`[WorkflowManagerTool] Filtrage par tags: ${tags.join(', ')}`);
workflows = workflows.filter((workflow: any) => {
if (!workflow.tags) return false;
return tags.some(tag => workflow.tags.includes(tag));
});
console.log(`[WorkflowManagerTool] ${workflows.length} workflows après filtrage`);
}
return {
success: true,
count: workflows.length,
workflows: workflows.map((wf: any) => ({
id: wf.id,
name: wf.name,
active: wf.active,
tags: wf.tags || [],
createdAt: wf.createdAt,
updatedAt: wf.updatedAt,
})),
};
} catch (error: any) {
return this.handleApiError(error, 'listWorkflows');
}
}
private async getWorkflow(workflowId: string) {
try {
const url = `${this.apiUrl}/workflows/${workflowId}`;
this.logApiCall('GET', url);
const response = await axios.get(url, this.getAxiosConfig());
console.log(`[WorkflowManagerTool] Workflow récupéré: ${response.data.name}`);
return {
success: true,
workflow: response.data,
};
} catch (error: any) {
return this.handleApiError(error, 'getWorkflow');
}
}
private async createWorkflow(workflowData: any) {
try {
const url = `${this.apiUrl}/workflows`;
this.logApiCall('POST', url, workflowData);
const response = await axios.post(url, workflowData, this.getAxiosConfig());
console.log(`[WorkflowManagerTool] Workflow créé: ${response.data.name}`);
return {
success: true,
workflow: response.data,
};
} catch (error: any) {
return this.handleApiError(error, 'createWorkflow');
}
}
private async updateWorkflow(workflowId: string, workflowData: any) {
try {
const url = `${this.apiUrl}/workflows/${workflowId}`;
this.logApiCall('PUT', url, workflowData);
const response = await axios.put(url, workflowData, this.getAxiosConfig());
console.log(`[WorkflowManagerTool] Workflow mis à jour: ${response.data.name}`);
return {
success: true,
workflow: response.data,
};
} catch (error: any) {
return this.handleApiError(error, 'updateWorkflow');
}
}
private async deleteWorkflow(workflowId: string) {
try {
const url = `${this.apiUrl}/workflows/${workflowId}`;
this.logApiCall('DELETE', url);
await axios.delete(url, this.getAxiosConfig());
console.log(`[WorkflowManagerTool] Workflow supprimé: ${workflowId}`);
return {
success: true,
message: `Workflow ${workflowId} supprimé avec succès`,
};
} catch (error: any) {
return this.handleApiError(error, 'deleteWorkflow');
}
}
private async exportWorkflow(workflowId: string, workflowPath: string) {
try {
// Récupérer le workflow
const getResult = await this.getWorkflow(workflowId);
if (!getResult.success || !('workflow' in getResult)) {
return getResult;
}
// Créer le répertoire de destination si nécessaire
const dir = path.dirname(workflowPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
// Écrire le workflow dans le fichier
fs.writeFileSync(workflowPath, JSON.stringify(getResult.workflow, null, 2));
console.log(`[WorkflowManagerTool] Workflow exporté vers: ${workflowPath}`);
return {
success: true,
message: `Workflow exporté avec succès vers ${workflowPath}`,
path: workflowPath,
};
} catch (error: any) {
console.error(`[WorkflowManagerTool] Erreur lors de l'exportation du workflow:`, error);
return {
success: false,
error: error.message,
details: "Erreur lors de l'exportation du workflow",
};
}
}
private async importWorkflow(workflowPath: string) {
try {
// Vérifier que le fichier existe
if (!fs.existsSync(workflowPath)) {
throw new Error(`Le fichier ${workflowPath} n'existe pas`);
}
// Lire le contenu du fichier
const workflowContent = fs.readFileSync(workflowPath, 'utf8');
const workflowData = JSON.parse(workflowContent);
console.log(`[WorkflowManagerTool] Workflow lu depuis: ${workflowPath}`);
// Supprimer l'ID pour créer un nouveau workflow
const { id, ...newWorkflowData } = workflowData;
// Créer le workflow
return this.createWorkflow(newWorkflowData);
} catch (error: any) {
console.error(`[WorkflowManagerTool] Erreur lors de l'importation du workflow:`, error);
return {
success: false,
error: error.message,
details: "Erreur lors de l'importation du workflow",
};
}
}
}
export default WorkflowManagerTool;