/** * Workflow Tools * MCP tools for searching and retrieving workflow information via API */ const workflowService = require('../services/workflow-service'); /** * List available workflow tools */ function listTools() { return [ { name: 'search_workflows', description: 'Search workflows by name. Returns matching workflows with metadata. Workflows are lazy-loaded from API on first request and cached for 1 hour, per application. Client-specific workflows (CST_* prefix) live in the "CustomApp" application — pass application: "CustomApp" to search them.', inputSchema: { type: 'object', additionalProperties: false, properties: { query: { type: 'string', description: 'Search query (searches in workflow name)', }, category: { type: 'string', description: 'Filter by the applicationName field of the returned workflows (workflows have no category field). Since `application` selects which application is fetched, all its workflows share the same applicationName — prefer `application` to change scope; `category` only narrows within the fetched set.', }, limit: { type: 'number', description: 'Maximum results to return (default: 50)', default: 50, }, application: { type: 'string', description: 'AD application whose workflows are searched (default: the active profile\'s application, usually EasyWMS). Client-specific workflows live in "CustomApp". Full list via list_workflow_categories.', }, }, }, }, { name: 'get_workflow_details', description: 'Get full details of a specific workflow by ID or name', inputSchema: { type: 'object', additionalProperties: false, properties: { workflow_id: { type: 'string', description: 'Workflow ID or exact name', }, application: { type: 'string', description: 'AD application the workflow belongs to (default: the active profile\'s application, usually EasyWMS). Client-specific workflows (CST_*) live in "CustomApp".', }, }, required: ['workflow_id'], }, }, { name: 'list_workflow_categories', description: 'List the AD applications declared on the tenant (Application/GetAll) with their workflow counts where already loaded. Workflows have no category field — the application is the only grouping. Use the `application` parameter of the workflow/AD tools to query a specific one (e.g. "CustomApp" for client-specific CST_* workflows).', inputSchema: { type: 'object', additionalProperties: false, properties: { application: { type: 'string', description: 'Load and count the workflows of this application (default: the active profile\'s application). Other applications are listed without loading them.', }, }, }, }, ]; } /** * Execute workflow tool */ async function executeTool(name, args) { try { switch (name) { case 'search_workflows': return await searchWorkflows(args); case 'get_workflow_details': return await getWorkflowDetails(args); case 'list_workflow_categories': return await listWorkflowCategories(args); default: throw new Error(`Unknown workflow tool: ${name}`); } } catch (error) { console.error(`[WorkflowTools] Error executing ${name}:`, error.message); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message, tool: name }, null, 2) }], isError: true }; } } /** * Tool: search_workflows */ async function searchWorkflows(args) { const { query, category, limit = 50, application } = args; console.error(`[WorkflowTools] Searching workflows: query="${query}", category="${category}", limit=${limit}, application=${application || '(profil)'}`); const results = await workflowService.searchWorkflows(query, category, limit, application); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...(application ? { application } : {}), count: results.length, // Clés réelles de l'API AD (minuscules, cf. D5) : id, name, version, // applicationName, commonInfo. Pas de code/category/description. workflows: results.map(w => { const commonInfo = w.commonInfo || w.CommonInfo || {}; return { id: w.id || w.Id, name: w.name || w.Name, applicationName: w.applicationName || w.ApplicationName, version: w.version || w.Version, createdBy: commonInfo.createdBy, createDate: commonInfo.createDate, updateDate: commonInfo.updateDate }; }) }, null, 2) }] }; } /** * Tool: get_workflow_details */ async function getWorkflowDetails(args) { const { workflow_id, application } = args; console.error(`[WorkflowTools] Getting workflow details: ${workflow_id} (application: ${application || '(profil)'})`); const workflow = await workflowService.getWorkflowDetails(workflow_id, application); return { content: [{ type: 'text', text: JSON.stringify({ success: true, workflow }, null, 2) }] }; } /** * Tool: list_workflow_categories */ async function listWorkflowCategories(args) { const { application } = args || {}; console.error(`[WorkflowTools] Listing applications (workflow groupings), application=${application || '(profil)'}`); // La liste vient d'Application/GetAll (9 applications sur le tenant mesuré), // pas des applicationName du seul cache actif (D26). Seule l'application // demandée (ou celle du profil) est chargée — pas de préchargement des // autres (D10) : leurs comptes n'apparaissent que si déjà en cache. const applications = await workflowService.fetchApplications(); const stats = await workflowService.getWorkflowStats(application); const cacheStatus = workflowService.getCacheStatus(); const enriched = applications.map(a => ({ name: a.name, version: a.version, ...(cacheStatus[a.name] ? { workflowCount: cacheStatus[a.name].count, cacheAge: cacheStatus[a.name].age } : { workflowCount: null }), })); return { content: [{ type: 'text', text: JSON.stringify({ success: true, note: 'Workflows have no category field in the AD API — the application is the only grouping. workflowCount is only known for applications already loaded (lazy loading); pass application to search_workflows/get_ad_elements to load one.', totalApplications: applications.length, applications: enriched, loaded: { application: stats.application, totalWorkflows: stats.total, cacheAge: stats.cacheAge } }, null, 2) }] }; } module.exports = { listTools, executeTool, };