166 lines
7.5 KiB
JavaScript
166 lines
7.5 KiB
JavaScript
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
|
|
};
|
|
}
|
|
}
|
|
);
|
|
}
|