agent-orchestration-mcp-server
Version:
MCP server that converts natural language queries to orchestration cards for GAFF
120 lines • 4.54 kB
JavaScript
/**
* Orchestration Card Validation
*
* Validate orchestration card structure and content
*
* Author: Sean Poyner <sean.poyner@pm.me>
*/
import { validateAgentsExist } from './gaff-config.js';
/**
* Validate orchestration card structure
*/
export function validateOrchestrationCard(card, gaffConfig) {
const errors = [];
const warnings = [];
// Check required top-level fields
if (!card.user_request) {
errors.push('Missing required field: user_request');
}
else {
if (!card.user_request.description) {
errors.push('Missing user_request.description');
}
}
if (!card.available_agents || !Array.isArray(card.available_agents)) {
errors.push('Missing or invalid field: available_agents (must be array)');
}
else if (card.available_agents.length === 0) {
warnings.push('No agents specified in available_agents');
}
// Validate agents
if (card.available_agents && Array.isArray(card.available_agents)) {
card.available_agents.forEach((agent, index) => {
if (!agent.name) {
errors.push(`Agent at index ${index} missing required field: name`);
}
if (!agent.type) {
errors.push(`Agent at index ${index} missing required field: type`);
}
if (!agent.capabilities || !Array.isArray(agent.capabilities)) {
errors.push(`Agent ${agent.name || index} missing required field: capabilities`);
}
if (!agent.input_schema) {
warnings.push(`Agent ${agent.name || index} missing input_schema`);
}
if (!agent.output_schema) {
warnings.push(`Agent ${agent.name || index} missing output_schema`);
}
});
}
// Validate against gaff.json if provided
if (gaffConfig && card.available_agents) {
const agentNames = card.available_agents
.filter((a) => a.name)
.map((a) => a.name);
const validation = validateAgentsExist(gaffConfig, agentNames);
if (!validation.valid) {
warnings.push(`Agents not found in gaff.json: ${validation.missing.join(', ')}`);
}
}
// Validate constraints if present
if (card.constraints) {
if (card.constraints.max_execution_time_ms &&
typeof card.constraints.max_execution_time_ms !== 'number') {
errors.push('constraints.max_execution_time_ms must be a number');
}
if (card.constraints.max_cost_per_execution &&
typeof card.constraints.max_cost_per_execution !== 'number') {
errors.push('constraints.max_cost_per_execution must be a number');
}
}
// Validate preferences if present
if (card.preferences) {
const validOptimizations = ['speed', 'cost', 'reliability', 'balanced'];
if (card.preferences.optimize_for &&
!validOptimizations.includes(card.preferences.optimize_for)) {
errors.push(`Invalid preferences.optimize_for: must be one of ${validOptimizations.join(', ')}`);
}
const validParallelization = ['none', 'conservative', 'balanced', 'aggressive'];
if (card.preferences.parallelization &&
!validParallelization.includes(card.preferences.parallelization)) {
errors.push(`Invalid preferences.parallelization: must be one of ${validParallelization.join(', ')}`);
}
}
return {
valid: errors.length === 0,
errors,
warnings,
};
}
/**
* Sanitize orchestration card (remove invalid fields, apply defaults)
*/
export function sanitizeOrchestrationCard(card) {
const sanitized = { ...card };
// Apply default constraints if missing
if (!sanitized.constraints) {
sanitized.constraints = {};
}
sanitized.constraints = {
max_execution_time_ms: 300000,
max_cost_per_execution: 10.0,
max_retries: 3,
...sanitized.constraints,
};
// Apply default preferences if missing
if (!sanitized.preferences) {
sanitized.preferences = {};
}
sanitized.preferences = {
optimize_for: 'balanced',
parallelization: 'balanced',
...sanitized.preferences,
};
// Ensure user_request has required fields
if (sanitized.user_request && !sanitized.user_request.success_criteria) {
sanitized.user_request.success_criteria = [];
}
return sanitized;
}
//# sourceMappingURL=validation.js.map