import { z } from "zod"; import { detectWorkflow } from "./detector.js"; import { getWorkflow, listWorkflows } from "./workflows/index.js"; /** * Register the orchestrate_task tool on the MCP server. */ export function registerOrchestrateTool(server) { server.tool( "orchestrate_task", "Provides workflow context, domain rules, and step-by-step guidance for Acai CMS tasks. " + "Returns relevant warnings, resource pointers, and suggested tool order. " + "Optional but recommended for multi-step tasks — helps avoid common mistakes. " + "Available workflows: create_section, populate_content, create_module, edit_module, " + "manage_records, manage_media, seo_setup, explore_site.", { task: z.string().describe( "The user's task or request in their own words. " + "Example: 'Crear una sección de productos con categorías e imágenes'" ), forceWorkflow: z.string().optional().describe( "Optional: force a specific workflow instead of auto-detecting. " + "Use when auto-detection is wrong or you know exactly which workflow to use. " + "Values: create_section, populate_content, create_module, edit_module, " + "manage_records, manage_media, seo_setup, explore_site" ) }, { readOnlyHint: true, destructiveHint: false }, async ({ task, forceWorkflow }) => { try { let workflowId; let confidence; let detectionInfo; if (forceWorkflow) { // Forced workflow — skip detection workflowId = forceWorkflow; confidence = 1.0; detectionInfo = { method: "forced", forceWorkflow }; } else { // Auto-detect workflow from task description const detection = detectWorkflow(task); if (!detection.workflow) { // No workflow matched — return general orientation return { content: [{ type: "text", text: JSON.stringify({ success: true, workflow: "none_detected", message: "Could not determine a specific workflow for this task. " + "You can proceed freely using available tools, or specify a workflow with forceWorkflow.", availableWorkflows: listWorkflows(), generalRules: [ "Table names WITHOUT 'cms_' prefix in all tool calls", "Primary key is ALWAYS 'num', never 'id'", "Upload fields are ALWAYS arrays of objects with urlPath property", "Use ONLY Twig FILTERS (pipe syntax), not Twig functions", "Date format: YYYY-MM-DD HH:mm:ss", "Checkbox values: 1 or 0 (number, not boolean)" ] }, null, 2) }] }; } if (detection.ambiguous) { // Ambiguous — return top suggestions const topWorkflow = getWorkflow(detection.workflow); const altWorkflows = detection.alternatives .map(a => getWorkflow(a.workflow)) .filter(Boolean); return { content: [{ type: "text", text: JSON.stringify({ success: true, workflow: "ambiguous", message: "Multiple workflows could match this task. " + "Pick the most appropriate one using forceWorkflow, or proceed with the top match.", topMatch: { id: topWorkflow.id, name: topWorkflow.name, description: topWorkflow.description, confidence: detection.confidence }, alternatives: altWorkflows.map((w, i) => ({ id: w.id, name: w.name, description: w.description, confidence: detection.alternatives[i].confidence })) }, null, 2) }] }; } workflowId = detection.workflow; confidence = detection.confidence; detectionInfo = { method: "auto", confidence: detection.confidence, alternatives: detection.alternatives }; } // Load the workflow const workflow = getWorkflow(workflowId); if (!workflow) { return { content: [{ type: "text", text: JSON.stringify({ success: false, error: `Unknown workflow: '${workflowId}'`, availableWorkflows: listWorkflows() }, null, 2) }], isError: true }; } // Build the response const response = { success: true, workflow: workflow.id, name: workflow.name, description: workflow.description, confidence, detection: detectionInfo, totalSteps: workflow.steps.length, steps: workflow.steps, context: workflow.context, rules: workflow.rules, warnings: workflow.warnings, resources: workflow.resources }; console.error(`[Orchestrator] Detected workflow: ${workflow.id} (confidence: ${confidence}) for task: "${task.substring(0, 80)}..."`); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] }; } catch (error) { console.error("[Orchestrator] Error:", error); return { content: [{ type: "text", text: JSON.stringify({ success: false, error: error.message }, null, 2) }], isError: true }; } } ); }