/** * Orchestrator MCP Server - Facade * * Main entry point for the orchestrator MCP server * Provides a clean API while hiding internal complexity */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import * as fs from 'fs'; import { frameworkLogger } from '../../core/framework-logger.js'; import { StatusHandler } from './handlers/status-handler.js'; import type { TaskHandler } from './handlers/task-handler.js'; import type { ComplexityHandler } from './handlers/complexity-handler.js'; import type { OrchestrationTask, OrchestrationResult } from './types.js'; import { spawnAside, closeAside, closeAllAsides, addObservations, getActiveAsideCount, getActiveAsideIds, getAsideState, extractGovernanceObservations, } from './aside-context.js'; /** * Orchestrator MCP Server * Enterprise-grade orchestration with advanced task management and agent coordination */ export class OrchestratorServer { private server: Server; private activeTasks: Map = new Map(); private taskHistory: Array<{ sessionId: string; description: string; tasks: number; result: OrchestrationResult; timestamp: string; }> = []; // Task and complexity handlers import the planner and nucleus. Load on tool call. private taskHandlerPromise: Promise | null = null; private complexityHandlerPromise: Promise | null = null; private statusHandler: StatusHandler; constructor() { this.statusHandler = new StatusHandler(); // Create MCP server this.server = new Server( { name: 'orchestrator', version: '1.14.1', }, { capabilities: { tools: {}, }, }, ); this.initializeTools(); this.setupToolHandlers(); frameworkLogger.log('orchestrator.server', 'initialize', 'info', { message: '0xRay Orchestrator MCP Server initialized', }); } private loadTaskHandler(): Promise { if (!this.taskHandlerPromise) { this.taskHandlerPromise = import('./handlers/task-handler.js') .then(({ TaskHandler }) => new TaskHandler()) .catch((error: unknown) => { this.taskHandlerPromise = null; throw error; }); } return this.taskHandlerPromise; } private loadComplexityHandler(): Promise { if (!this.complexityHandlerPromise) { this.complexityHandlerPromise = import('./handlers/complexity-handler.js') .then(({ ComplexityHandler }) => new ComplexityHandler()) .catch((error: unknown) => { this.complexityHandlerPromise = null; throw error; }); } return this.complexityHandlerPromise; } /** * Initialize tool definitions */ private initializeTools(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'orchestrate-task', description: 'Execute complex multi-step tasks with intelligent agent delegation and progress tracking', inputSchema: { type: 'object', properties: { description: { type: 'string' }, tasks: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, description: { type: 'string' }, type: { type: 'string' }, priority: { type: 'string', enum: ['critical', 'high', 'medium', 'low'], default: 'medium', }, dependencies: { type: 'array', items: { type: 'string' }, }, estimatedComplexity: { type: 'number', minimum: 1, maximum: 100, }, }, required: ['id', 'description', 'type'], }, }, sessionId: { type: 'string' }, executionMode: { type: 'string', enum: ['parallel', 'sequential', 'optimized'], default: 'optimized', }, timeout: { type: 'number', default: 300000 }, confer: { type: 'boolean', description: 'Run mandatory 3-agent confer quorum (researcher, architect-tools, code-review) instead of task execution', }, conferFixture: { type: 'boolean', description: 'Use fixture confer receipts (CI/verify)', }, collocatedText: { type: 'string', description: 'Collocated synthesis context for confer prompts', }, userAsideId: { type: 'string', description: 'Activate an existing user aside for spawn routing', }, setActiveAside: { type: 'boolean', description: 'Set userAsideId as the active aside (default true)', }, clearActiveAside: { type: 'boolean', description: 'Clear active aside — resume main lead-dev plan spawns', }, }, required: ['description'], }, }, { name: 'analyze-complexity', description: 'Analyze task complexity and recommend orchestration strategy. When lead_dev_mode is on (features.json), includes phased plan + todos + subagent routes. Pass userAsideId to intake a parallel user aside ({asideId}.a.* namespaced todos, worktree-like). During synthesis checkpoint, returns collocated context and mandatory-consult realignment plan — pass sessionId from the active session.', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Active session id (required to clear synthesis checkpoint; inferred from checkpoint state when omitted)', }, userAsideId: { type: 'string', description: 'User aside id — persists .xray/state/asides/{id}.json with {asideId}.a.* todos (parallel track)', }, userAsideTitle: { type: 'string', description: 'Display title for the user aside (defaults to userAsideId)', }, worktree: { type: 'string', description: 'Optional worktree path for aside isolation', }, branch: { type: 'string', description: 'Optional git branch for the aside', }, setActiveAside: { type: 'boolean', description: 'Activate aside for spawn routing (default true when userAsideId set)', }, tasks: { type: 'array', items: { type: 'object', properties: { description: { type: 'string' }, type: { type: 'string' }, files: { type: 'array', items: { type: 'string' } }, dependencyCount: { type: 'number', description: 'Upstream dependency count (complexity hint only)', }, dependencies: { type: 'array', items: { type: 'string' }, description: 'Task IDs this task depends on', }, riskLevel: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], }, }, }, }, }, required: ['tasks'], }, }, { name: 'get-orchestration-status', description: 'Get comprehensive status of active orchestrations and agent utilization', inputSchema: { type: 'object', properties: { sessionId: { type: 'string' }, detailed: { type: 'boolean', default: false }, }, }, }, { name: 'cancel-orchestration', description: 'Cancel active orchestration tasks with cleanup', inputSchema: { type: 'object', properties: { sessionId: { type: 'string' }, taskId: { type: 'string' }, force: { type: 'boolean', default: false }, }, }, }, { name: 'optimize-orchestration', description: 'Analyze and optimize orchestration patterns for better performance', inputSchema: { type: 'object', properties: { history: { type: 'boolean', default: true }, recommendations: { type: 'boolean', default: true }, }, }, }, { name: 'govern-and-apply', description: 'Govern inference proposals through weighted voting, then apply approved ones via agent delegation', inputSchema: { type: 'object', properties: { proposals: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, title: { type: 'string' }, description: { type: 'string' }, type: { type: 'string', enum: ['fix', 'refactor', 'guard', 'automate', 'codify'], }, confidence: { type: 'number', minimum: 0, maximum: 1 }, evidence: { type: 'array', items: { type: 'string' } }, }, required: ['id', 'title', 'description', 'type', 'confidence', 'evidence'], }, }, skipApply: { type: 'boolean', default: false }, }, required: ['proposals'], }, }, ], }; }); } /** * Set up tool request handlers */ private setupToolHandlers(): void { this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; switch (name) { case 'orchestrate-task': { const orchArgs = args as { description: string; tasks?: OrchestrationTask[]; sessionId?: string; executionMode?: string; timeout?: number; }; const aside = await spawnAside({ description: `Orchestration: ${orchArgs.description}`, ...(orchArgs.sessionId ? { sessionId: orchArgs.sessionId } : {}), inheritedContext: { taskCount: orchArgs.tasks?.length ?? 0, executionMode: orchArgs.executionMode ?? 'optimized' }, }); try { const taskHandler = await this.loadTaskHandler(); const result = await taskHandler.handleOrchestrateTask( orchArgs, { taskHistory: this.taskHistory, activeTasks: this.activeTasks, asideId: aside.asideId }, ); return result; } finally { closeAside(aside.asideId); } } case 'analyze-complexity': { const complexityArgs = args as { tasks: OrchestrationTask[]; sessionId?: string; }; const projectRoot = process.cwd(); const { isSynthesisCheckpointDue, getSynthesisDueReason, getSynthesisCheckpointSessionId, } = await import('../../nucleus/synthesis.js'); const { appendSynthesisContextToResponse, prepareSynthesisCollocatedContext } = await import('../../nucleus/synthesis-context.js'); const { loadPersistedLeadDevPlan, isSynthesisRealignmentPlan, areSynthesisConsultTodosComplete, getSynthesisConsultTodos, getNextRequiredTodo, } = await import('../../nucleus/lead-dev-plan-persistence.js'); const checkpointSession = getSynthesisCheckpointSessionId(projectRoot); if ( checkpointSession && complexityArgs.sessionId && complexityArgs.sessionId !== checkpointSession ) { return { content: [ { type: 'text' as const, text: `❌ Synthesis sessionId mismatch — expected \`${checkpointSession}\` ` + `(from synthesis-checkpoint.json), got \`${complexityArgs.sessionId}\`.`, }, ], }; } const sessionId = checkpointSession ?? complexityArgs.sessionId ?? null; const synthesisDue = checkpointSession ? isSynthesisCheckpointDue(projectRoot, checkpointSession) : false; const dueReason = synthesisDue ? getSynthesisDueReason(projectRoot, sessionId) : null; if (synthesisDue && !sessionId) { return { content: [ { type: 'text' as const, text: '❌ Synthesis checkpoint due but sessionId could not be resolved. ' + 'Pass sessionId matching .xray/state/synthesis-checkpoint.json.', }, ], }; } const existingPlan = loadPersistedLeadDevPlan(projectRoot); const realignmentInProgress = synthesisDue && existingPlan && isSynthesisRealignmentPlan(existingPlan) && !areSynthesisConsultTodosComplete(existingPlan); if (realignmentInProgress) { const collocated = await prepareSynthesisCollocatedContext(projectRoot, dueReason); const nextTodo = getNextRequiredTodo(existingPlan); const consultTodos = getSynthesisConsultTodos(existingPlan); const todoList = consultTodos .map( (t) => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.id} (${t.subagent}): ${t.task}`, ) .join('\n'); let text = '🔄 Synthesis realignment in progress — consult todos preserved (analyze-complexity no-op).\n\n' + `**Next required todo:** ${nextTodo?.id ?? 'none'} (${nextTodo?.subagent ?? ''})\n\n` + `${todoList}\n\n` + 'Complete mandatory consult todos via Task spawns before other gated work.'; if (collocated.collatedText) { text = appendSynthesisContextToResponse( [{ type: 'text', text }], collocated.collatedText, )[0]!.text; } return { content: [{ type: 'text' as const, text }] }; } const collocated = synthesisDue ? await prepareSynthesisCollocatedContext(projectRoot, dueReason) : null; const inheritedContext = synthesisDue && collocated ? { synthesis: true, ...collocated } : { taskCount: complexityArgs.tasks.length }; const aside = await spawnAside({ description: synthesisDue ? 'Synthesis checkpoint: reflect & realign' : `Complexity analysis: ${complexityArgs.tasks.length} tasks`, ...(sessionId ? { sessionId } : {}), inheritedContext, }); try { const complexityHandler = await this.loadComplexityHandler(); const result = await complexityHandler.handleAnalyzeComplexity( { ...complexityArgs, ...(sessionId ? { sessionId } : {}), ...(synthesisDue ? { synthesisCheckpoint: true, synthesisDueReason: dueReason, collocatedText: collocated?.collatedText ?? null, conferFixture: process.env.XRAY_CONFER_FIXTURE === '1', } : {}), }, aside.asideId, ); let content = result.content; if (synthesisDue && collocated?.collatedText) { content = appendSynthesisContextToResponse(content, collocated.collatedText); } if (synthesisDue && result.ok && !result.content[0]?.text?.includes('Confer quorum')) { content = content.map((block, index) => index === 0 ? { ...block, text: `${block.text}\n\n**Confer** runs automatically at synthesis checkpoint (researcher → architect-tools → code-review). Checkpoint clears when all consult todos complete.`, } : block, ); } return { content }; } finally { closeAside(aside.asideId); } } case 'get-orchestration-status': return await this.statusHandler.handleGetOrchestrationStatus(args as { sessionId?: string; detailed?: boolean; }, { taskHistory: this.taskHistory, activeTasks: this.activeTasks, asideCount: getActiveAsideCount(), asideIds: getActiveAsideIds(), }); case 'cancel-orchestration': { const cancelArgs = args as { sessionId?: string; taskId?: string; force?: boolean }; if (cancelArgs.force || (!cancelArgs.sessionId && !cancelArgs.taskId)) { closeAllAsides(); } else if (cancelArgs.sessionId) { const asideIds = getActiveAsideIds().filter( (id) => getAsideState(id)?.sessionId === cancelArgs.sessionId, ); for (const id of asideIds) closeAside(id); } return await this.statusHandler.handleCancelOrchestration(cancelArgs, { taskHistory: this.taskHistory, activeTasks: this.activeTasks, }); } case 'optimize-orchestration': return await this.statusHandler.handleOptimizeOrchestration(args as { history?: boolean; recommendations?: boolean; }); case 'govern-and-apply': return await this.handleGovernAndApply(args as { proposals: Array<{ id: string; title: string; description: string; type: 'fix' | 'refactor' | 'guard' | 'automate' | 'codify'; confidence: number; evidence: string[]; }>; skipApply?: boolean; }); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [{ type: 'text' as const, text: `Error handling tool '${request.params.name}': ${error instanceof Error ? error.message : String(error)}`, }], }; } }); } private async handleGovernAndApply(args: { proposals: Array<{ id: string; title: string; description: string; type: 'fix' | 'refactor' | 'guard' | 'automate' | 'codify'; confidence: number; evidence: string[]; }>; skipApply?: boolean; }): Promise<{ content: Array<{ type: 'text'; text: string }> }> { try { const projectRoot = process.cwd(); const { isSynthesisCheckpointDue, getSynthesisDueReason, getSynthesisCheckpointSessionId, recordExecutionSlice, } = await import('../../nucleus/synthesis.js'); const { loadPersistedLeadDevPlan, isSynthesisRealignmentPlan, areSynthesisConsultTodosComplete, } = await import('../../nucleus/lead-dev-plan-persistence.js'); const checkpointSession = getSynthesisCheckpointSessionId(projectRoot); const sessionId = checkpointSession ?? null; if (sessionId && isSynthesisCheckpointDue(projectRoot, sessionId)) { const dueReason = getSynthesisDueReason(projectRoot, sessionId); const plan = loadPersistedLeadDevPlan(projectRoot); const realignmentPending = plan && isSynthesisRealignmentPlan(plan) && !areSynthesisConsultTodosComplete(plan); return { content: [{ type: 'text' as const, text: JSON.stringify({ blocked: true, gate: 'synthesis-checkpoint', dueReason, primitive: 'synthesis', realignmentPending, hint: realignmentPending ? 'Complete mandatory consult todos (s.1–s.3) before govern-and-apply' : 'Run analyze-complexity to reflect and realign before govern-and-apply', }, null, 2), }], }; } const { InferenceCycle } = await import('../../inference/inference-cycle.js'); const cycle = InferenceCycle.getInstance(projectRoot, { skipApply: args.skipApply ?? false, }); const aside = await spawnAside({ description: `Governance cycle: ${args.proposals.length} proposals`, ...(sessionId ? { sessionId } : {}), priorVerdictContext: { proposalCount: args.proposals.length }, }); let result: import('../../inference/inference-cycle.js').InferenceCycleResult; try { result = await cycle.governExternalProposals(args.proposals as any); addObservations(aside.asideId, extractGovernanceObservations(result)); } finally { closeAside(aside.asideId); } const approved = result!.votes.filter((v) => v.decision === 'approve').length; const rejected = result!.votes.length - approved; if (sessionId) { recordExecutionSlice('gate', { projectRoot, sessionId }); } return { content: [{ type: 'text' as const, text: JSON.stringify({ cycleId: result!.cycleId, asideId: aside.asideId, synthesisBridged: Boolean(sessionId), approved, rejected, votes: result!.votes, proposals: result!.proposals.map((p) => ({ id: p.id, title: p.title, type: p.type, status: p.status, })), duration: result!.duration, }, null, 2), }], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Govern-and-apply failed: ${error instanceof Error ? error.message : String(error)}`, }], }; } } /** * Start the server */ async start(): Promise { const transport = new StdioServerTransport(); await this.server.connect(transport); await frameworkLogger.log('orchestrator.server', 'start', 'info', { message: '0xRay Orchestrator MCP Server started', }); } /** * Stop the server */ async stop(): Promise { closeAllAsides(); await frameworkLogger.log('orchestrator.server', 'shutdown', 'info', { message: '0xRay Orchestrator MCP Server shutting down', }); await this.server.close(); } } // Export singleton factory let serverInstance: OrchestratorServer | null = null; export function createOrchestratorServer(): OrchestratorServer { if (!serverInstance) { serverInstance = new OrchestratorServer(); } return serverInstance; } // Run if called directly if (import.meta.url === `file://${fs.realpathSync(process.argv[1]!)}`) { const server = createOrchestratorServer(); server.start().catch((error) => { frameworkLogger.log('orchestrator.server', 'fatal-error', 'error', { message: error instanceof Error ? error.message : String(error), }); process.exit(1); }); }