Initial commit
This commit is contained in:
165
mcp-server/tools/orchestrator/orchestrate.js
Normal file
165
mcp-server/tools/orchestrator/orchestrate.js
Normal file
@@ -0,0 +1,165 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user