mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing advanced tools, resources, and prompts for implementing AI agent best practices
183 lines • 6.48 kB
JavaScript
/**
* AgentOrchestrator - Manages agent handoffs and workflow execution
*
* @module agents/orchestrator
*/
import { ErrorCode } from "../tools/shared/error-codes.js";
import { McpToolError } from "../tools/shared/errors.js";
import { executionGraph } from "./execution-graph.js";
import { agentRegistry } from "./registry.js";
/**
* Orchestrator for managing agent handoffs and multi-step workflows.
* Coordinates execution between agents and manages data flow.
*/
export class AgentOrchestrator {
toolExecutor;
/**
* Sets the tool executor function for invoking MCP tools.
*
* @param executor - Function that executes an MCP tool by name with arguments
*/
setToolExecutor(executor) {
this.toolExecutor = executor;
}
/**
* Executes a handoff to a target agent.
*
* @param request - The handoff request containing target agent and context
* @returns Result of the handoff including success status and output
*/
async executeHandoff(request) {
const startTime = Date.now();
// Get target agent
const agent = agentRegistry.getAgent(request.targetAgent);
if (!agent) {
const executionTime = Date.now() - startTime;
const error = `Agent not found: ${request.targetAgent}`;
// Record failed handoff
executionGraph.recordHandoff({
sourceAgent: request.sourceAgent,
targetAgent: request.targetAgent,
executionTime,
success: false,
error,
});
return {
success: false,
output: null,
executionTime,
error,
};
}
try {
if (!this.toolExecutor) {
throw new McpToolError(ErrorCode.CONFIG_INVALID, "Tool executor not configured", { targetAgent: request.targetAgent });
}
// Execute the backing tool
const output = await this.toolExecutor(agent.toolName, request.context);
const executionTime = Date.now() - startTime;
// Record successful handoff
executionGraph.recordHandoff({
sourceAgent: request.sourceAgent,
targetAgent: request.targetAgent,
executionTime,
success: true,
});
return {
success: true,
output,
executionTime,
};
}
catch (error) {
const executionTime = Date.now() - startTime;
const errorMessage = error instanceof McpToolError
? error.message
: error instanceof Error
? error.message
: "Unknown error";
// Record failed handoff
executionGraph.recordHandoff({
sourceAgent: request.sourceAgent,
targetAgent: request.targetAgent,
executionTime,
success: false,
error: errorMessage,
});
return {
success: false,
output: null,
executionTime,
error: errorMessage,
};
}
}
/**
* Executes a multi-step workflow with sequential agent handoffs.
*
* @param workflow - The workflow definition containing steps
* @param input - Initial input data for the first step
* @returns Result of the workflow execution including all step outputs
*/
async executeWorkflow(workflow, input) {
const startTime = Date.now();
const outputs = { _initial: input };
const steps = [];
let currentInput = input;
for (let i = 0; i < workflow.steps.length; i++) {
const step = workflow.steps[i];
// Map input from previous outputs if specified
const stepInput = step.inputMapping
? this.mapInput(outputs, step.inputMapping)
: currentInput;
const result = await this.executeHandoff({
sourceAgent: i > 0 ? workflow.steps[i - 1].agent : undefined,
targetAgent: step.agent,
context: stepInput,
});
steps.push({
agent: step.agent,
success: result.success,
output: result.output,
executionTime: result.executionTime,
error: result.error,
});
if (!result.success) {
return {
success: false,
outputs,
executionTime: Date.now() - startTime,
steps,
error: `Workflow failed at step: ${step.agent}`,
};
}
outputs[step.agent] = result.output;
currentInput = result.output;
}
return {
success: true,
outputs,
executionTime: Date.now() - startTime,
steps,
};
}
/**
* Maps input values from previous outputs based on mapping configuration.
*
* @param outputs - Record of previous outputs from workflow steps
* @param mapping - Mapping configuration with keys to path expressions
* @returns Mapped input object
*/
mapInput(outputs, mapping) {
const result = {};
for (const [key, path] of Object.entries(mapping)) {
result[key] = this.getValueByPath(outputs, path);
}
return result;
}
/**
* Retrieves a value from a nested object using a dot-notation path.
*
* @param obj - The object to traverse
* @param path - Dot-separated path to the value (e.g., "user.name")
* @returns The value at the path, or undefined if not found
*/
getValueByPath(obj, path) {
return path.split(".").reduce((acc, key) => {
if (acc === null || typeof acc !== "object") {
return undefined;
}
const record = acc;
if (!(key in record)) {
return undefined;
}
return record[key];
}, obj);
}
}
/**
* Singleton instance of the AgentOrchestrator.
* Use this export for global agent orchestration.
*/
export const agentOrchestrator = new AgentOrchestrator();
//# sourceMappingURL=orchestrator.js.map