UNPKG

claude-code-subagents-orchestrator

Version:

Claude Code Sub-agents Orchestrator - A powerful MCP server for orchestrating multiple AI sub-agents for complex task execution in Claude Code

1,094 lines (895 loc) 31.7 kB
# Developer Guide for System Extension This guide provides comprehensive information for developers who want to extend, customize, or contribute to the Claude Code Subagents Orchestrator. ## Architecture Overview ### Core Components The orchestrator is built with a modular architecture designed for extensibility: ``` ┌─────────────────────────────────────────────────────────────┐ MCP Server Layer ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ Tool Registry Core MCP Tools Delegation Tools └─────────────────┘ └─────────────────┘ └──────────────┘ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ Delegation Engine ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ Task Classifier Agent Router Delegation Interceptor └─────────────────┘ └─────────────────┘ └──────────────┘ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ Agent Management ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ Agent Spec Prompt Workflow Parser Generator Engine └─────────────────┘ └─────────────────┘ └──────────────┘ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ Infrastructure Layer ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ File System Bootstrap Path Manager System Resolver └─────────────────┘ └─────────────────┘ └──────────────┘ └─────────────────────────────────────────────────────────────┘ ``` ### Design Principles 1. **Modularity**: Each component has a single responsibility 2. **Extensibility**: Clear interfaces for adding new functionality 3. **Type Safety**: Full TypeScript coverage with runtime validation 4. **Error Resilience**: Comprehensive error handling and recovery 5. **Performance**: Lazy loading and efficient resource management ## Development Environment Setup ### Prerequisites ```bash # Node.js 18+ required node --version # Should be 18.0.0 or higher # Install dependencies npm install # Install development dependencies npm install --save-dev typescript @types/node jest ts-jest ``` ### Project Structure ``` claude-code-subagents-orchestrator/ ├── src/ ├── core/ # Core functionality ├── agent-router.ts ├── delegation-interceptor.ts ├── task-classifier.ts └── workflow.ts ├── tools/ # MCP tool implementations ├── index.ts └── delegation-tools.ts ├── types/ # TypeScript type definitions ├── core.ts ├── enhanced-core.ts └── tools.ts ├── server.ts # Main MCP server └── index.ts # Entry point ├── tests/ # Test suites ├── docs/ # Documentation ├── examples/ # Usage examples └── scripts/ # Build and utility scripts ``` ### Build System ```bash # Development build with watch npm run dev # Production build npm run build # Type checking npm run typecheck # Linting npm run lint npm run lint:fix # Testing npm test npm run test:watch npm run test:coverage ``` ## Creating Custom Tools ### Tool Interface All tools must implement the `ITool` interface: ```typescript interface ITool { name: string; description: string; execute(params: any): Promise<ToolResult>; validate(params: any): boolean; } ``` ### Basic Tool Example ```typescript import { z } from 'zod'; import { ITool, ToolResult, OrchestratorError, ErrorCode } from '../types/core.js'; // Define parameter schema const CustomToolParamsSchema = z.object({ input: z.string(), options: z.object({ format: z.enum(['json', 'text']).default('json'), verbose: z.boolean().default(false) }).optional() }); type CustomToolParams = z.infer<typeof CustomToolParamsSchema>; export class CustomTool implements ITool { name = 'custom_tool'; description = 'A custom tool for demonstration purposes'; validate(params: any): boolean { try { CustomToolParamsSchema.parse(params); return true; } catch { return false; } } async execute(params: CustomToolParams): Promise<ToolResult> { const startTime = Date.now(); try { // Validate parameters const validatedParams = CustomToolParamsSchema.parse(params); // Implement tool logic const result = await this.processInput(validatedParams.input, validatedParams.options); return { success: true, data: result, metadata: { executionTime: Date.now() - startTime, timestamp: new Date(), toolName: this.name } }; } catch (error) { throw new OrchestratorError( ErrorCode.TOOL_EXECUTION_FAILED, `Custom tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`, error, true ); } } private async processInput(input: string, options?: any): Promise<any> { // Implement your custom logic here return { processed: input.toUpperCase(), timestamp: new Date(), options }; } } ``` ### Advanced Tool with Delegation ```typescript import { AgentRouter } from '../core/agent-router.js'; import { TaskClassifier } from '../core/task-classifier.js'; export class DelegatingCustomTool implements ITool { name = 'delegating_custom_tool'; description = 'A custom tool that can delegate to specialist agents'; constructor( private agentRouter: AgentRouter, private taskClassifier: TaskClassifier ) {} validate(params: any): boolean { return params && typeof params.task === 'string'; } async execute(params: any): Promise<ToolResult> { const startTime = Date.now(); try { // Classify the task to determine if delegation is needed const classification = await this.taskClassifier.classifyTask({ method: 'custom_tool', params: { task: params.task } }); if (classification.delegationRequired) { // Delegate to specialist agent const delegation = await this.agentRouter.executeWithAgent( classification.suggestedAgent, { method: 'custom_tool', params: { task: params.task, delegated: true, classification } }, `custom_tool_${Date.now()}`, true // Enforced delegation ); return { success: true, data: { result: delegation.output, delegated: true, agent: classification.suggestedAgent, classification }, metadata: { executionTime: Date.now() - startTime, timestamp: new Date(), toolName: this.name } }; } else { // Handle directly const result = await this.handleDirectly(params.task); return { success: true, data: { result, delegated: false, handledDirectly: true }, metadata: { executionTime: Date.now() - startTime, timestamp: new Date(), toolName: this.name } }; } } catch (error) { throw new OrchestratorError( ErrorCode.TOOL_EXECUTION_FAILED, `Delegating custom tool failed: ${error instanceof Error ? error.message : 'Unknown error'}`, error, true ); } } private async handleDirectly(task: string): Promise<any> { // Direct handling for non-specialist tasks return { message: `Handled task directly: ${task}`, timestamp: new Date() }; } } ``` ### Registering Custom Tools ```typescript import { ToolRegistry } from '../tools/index.js'; import { CustomTool, DelegatingCustomTool } from './custom-tools.js'; // Register tools in the server export function registerCustomTools( registry: ToolRegistry, agentRouter: AgentRouter, taskClassifier: TaskClassifier ) { registry.register(new CustomTool()); registry.register(new DelegatingCustomTool(agentRouter, taskClassifier)); } ``` ## Adding New Agent Types ### Agent Specification Format Agents are defined using structured markdown files: ```markdown # Custom Specialist Agent ## Metadata - name: custom-specialist - version: 1.0.0 - description: Specialized agent for custom domain expertise - category: custom - complexity: medium - tags: [custom, specialized, domain-specific] ## Capabilities - tools: [custom-tool-1, custom-tool-2, analysis-tool] - languages: [JavaScript, TypeScript, Python] - frameworks: [Custom-Framework, Domain-Specific-Library] - domains: [custom-domain, specialized-analysis] ## Focus Areas - Custom domain analysis and optimization - Specialized problem-solving approaches - Integration with domain-specific tools and systems - Best practices for custom domain implementations ## Approach 1. Analyze requirements within custom domain context 2. Apply domain-specific methodologies and patterns 3. Leverage specialized tools and frameworks 4. Provide expert-level recommendations and implementations 5. Ensure compliance with domain standards and practices ## Constraints - Specialized in custom domain - defer other types of work - Requires domain-specific context for optimal results - May need integration with specialized external tools - Focus on expert-level solutions rather than basic implementations ## System Prompt You are Claude Code, Anthropic's official CLI for Claude. You are a Custom Specialist Agent with deep expertise in [custom domain]. Your role is to provide expert-level analysis, design, and implementation for [domain-specific] challenges. ### Your Expertise - [Domain-specific skill 1] - [Domain-specific skill 2] - [Domain-specific skill 3] - [Integration patterns and best practices] ### Your Approach - Always start with requirements analysis specific to [custom domain] - Apply proven [domain-specific] methodologies and patterns - Provide detailed explanations of your reasoning - Include relevant [domain-specific] best practices - Consider scalability and maintainability within [domain context] ### Your Constraints - Stay within your area of expertise ([custom domain]) - Defer non-[domain] tasks to appropriate specialists - Always provide expert-level solutions, not basic implementations - Include validation and testing approaches specific to [domain] ## Workflow Configuration - maxSteps: 10 - timeoutMs: 900000 - maxRetries: 2 - backoffMs: 2000 - requiresValidation: true - supportedComplexityLevels: [medium, high] ``` ### Agent Classification Rules Add classification rules for new agent types: ```typescript // In task-classifier.ts export class TaskClassifier { private domainRules: Map<string, ClassificationRule[]> = new Map([ // Existing rules... // Custom domain rules ['custom-domain', [ { pattern: /custom.domain|specialized.analysis|domain.specific/i, agent: 'custom-specialist', confidence: 0.9, keywords: ['custom-domain', 'specialized', 'domain-specific'], complexity: 'medium' }, { pattern: /advanced.custom|expert.analysis|complex.domain/i, agent: 'custom-specialist', confidence: 0.95, keywords: ['advanced', 'expert', 'complex'], complexity: 'high' } ]] ]); } ``` ### Agent Router Integration Register new agents with the router: ```typescript // In agent-router.ts export class AgentRouter { private agentConfigs: Map<string, AgentSpawnConfig> = new Map([ // Existing configurations... ['custom-specialist', { command: 'node', args: ['./agents/custom-specialist/server.js'], env: { AGENT_TYPE: 'custom-specialist', DOMAIN: 'custom-domain', EXPERTISE_LEVEL: 'expert' }, timeout: 900000, // 15 minutes maxRetries: 2, healthCheckInterval: 60000 }] ]); } ``` ## Extending the Delegation System ### Custom Delegation Rules Add custom delegation logic: ```typescript import { DelegationRule, DelegationContext } from '../types/enhanced-core.js'; export class CustomDelegationRules { static createCustomRule( domain: string, pattern: string, targetAgent: string, priority: number = 5 ): DelegationRule { return { domain, pattern: new RegExp(pattern, 'i'), targetAgent, priority, enforcementLevel: 'strict', bypassProtection: true, validationRequired: true }; } static evaluateCustomContext(context: DelegationContext): boolean { // Custom logic for delegation decisions const task = context.originalRequest.params?.task || ''; // Example: Force delegation for certain keywords const forceKeywords = ['enterprise', 'production', 'critical']; if (forceKeywords.some(keyword => task.toLowerCase().includes(keyword))) { context.enforcementLevel = 'strict'; return true; } // Example: Advisory for experimental features if (task.toLowerCase().includes('experimental')) { context.enforcementLevel = 'advisory'; return false; } return context.detectedIntent.delegationRequired; } } ``` ### Custom Interceptor Extensions Extend the delegation interceptor: ```typescript export class ExtendedDelegationInterceptor extends DelegationInterceptor { private customPreProcessors: Array<(request: MCPRequest) => MCPRequest> = []; private customPostProcessors: Array<(response: MCPResponse) => MCPResponse> = []; addPreProcessor(processor: (request: MCPRequest) => MCPRequest) { this.customPreProcessors.push(processor); } addPostProcessor(processor: (response: MCPResponse) => MCPResponse) { this.customPostProcessors.push(processor); } async interceptRequest(request: MCPRequest): Promise<MCPResponse> { // Apply custom pre-processors let processedRequest = request; for (const processor of this.customPreProcessors) { processedRequest = processor(processedRequest); } // Standard interception logic let response = await super.interceptRequest(processedRequest); // Apply custom post-processors for (const processor of this.customPostProcessors) { response = processor(response); } return response; } } // Usage example const interceptor = new ExtendedDelegationInterceptor(); interceptor.addPreProcessor((request) => { // Add custom headers or modify request return { ...request, params: { ...request.params, customProcessed: true, timestamp: Date.now() } }; }); interceptor.addPostProcessor((response) => { // Add custom response metadata return { ...response, metadata: { ...response.metadata, customProcessed: true, processingTime: Date.now() } }; }); ``` ## Plugin System ### Plugin Interface Create a plugin system for modular extensions: ```typescript export interface IPlugin { name: string; version: string; description: string; initialize(orchestrator: OrchestratorMCPServer): Promise<void>; cleanup(): Promise<void>; // Optional hooks onToolRegistration?(toolRegistry: ToolRegistry): void; onAgentRegistration?(agentRouter: AgentRouter): void; onDelegationIntercept?(interceptor: DelegationInterceptor): void; } export abstract class BasePlugin implements IPlugin { abstract name: string; abstract version: string; abstract description: string; async initialize(orchestrator: OrchestratorMCPServer): Promise<void> { // Default implementation } async cleanup(): Promise<void> { // Default implementation } } ``` ### Example Plugin ```typescript export class CustomDomainPlugin extends BasePlugin { name = 'custom-domain-plugin'; version = '1.0.0'; description = 'Plugin for custom domain specialist integration'; async initialize(orchestrator: OrchestratorMCPServer): Promise<void> { console.log(`Initializing ${this.name} v${this.version}`); // Register custom tools if (orchestrator.toolRegistry) { this.onToolRegistration(orchestrator.toolRegistry); } // Register custom agents if (orchestrator.agentRouter) { this.onAgentRegistration(orchestrator.agentRouter); } // Extend delegation system if (orchestrator.delegationInterceptor) { this.onDelegationIntercept(orchestrator.delegationInterceptor); } } onToolRegistration(toolRegistry: ToolRegistry): void { // Register custom domain tools toolRegistry.register(new CustomDomainAnalysisTool()); toolRegistry.register(new CustomDomainOptimizationTool()); } onAgentRegistration(agentRouter: AgentRouter): void { // Register custom domain agents agentRouter.registerAgent('custom-domain-specialist', { command: 'node', args: ['./plugins/custom-domain/agent-server.js'], env: { DOMAIN: 'custom-domain' } }); } onDelegationIntercept(interceptor: DelegationInterceptor): void { // Add custom delegation rules interceptor.addRule('custom-domain', { pattern: /custom.domain|specialized.analysis/i, targetAgent: 'custom-domain-specialist', priority: 15, enforcementLevel: 'strict' }); } } ``` ### Plugin Manager ```typescript export class PluginManager { private plugins: Map<string, IPlugin> = new Map(); private orchestrator: OrchestratorMCPServer; constructor(orchestrator: OrchestratorMCPServer) { this.orchestrator = orchestrator; } async loadPlugin(plugin: IPlugin): Promise<void> { if (this.plugins.has(plugin.name)) { throw new Error(`Plugin ${plugin.name} is already loaded`); } try { await plugin.initialize(this.orchestrator); this.plugins.set(plugin.name, plugin); console.log(`Plugin ${plugin.name} loaded successfully`); } catch (error) { console.error(`Failed to load plugin ${plugin.name}:`, error); throw error; } } async unloadPlugin(pluginName: string): Promise<void> { const plugin = this.plugins.get(pluginName); if (!plugin) { throw new Error(`Plugin ${pluginName} is not loaded`); } try { await plugin.cleanup(); this.plugins.delete(pluginName); console.log(`Plugin ${pluginName} unloaded successfully`); } catch (error) { console.error(`Failed to unload plugin ${pluginName}:`, error); throw error; } } getLoadedPlugins(): string[] { return Array.from(this.plugins.keys()); } async cleanup(): Promise<void> { for (const [name, plugin] of this.plugins) { try { await plugin.cleanup(); } catch (error) { console.error(`Error cleaning up plugin ${name}:`, error); } } this.plugins.clear(); } } ``` ## Testing Framework ### Unit Test Structure ```typescript import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import { CustomTool } from '../src/tools/custom-tool.js'; describe('CustomTool', () => { let tool: CustomTool; beforeEach(() => { tool = new CustomTool(); }); afterEach(() => { // Cleanup if needed }); describe('validation', () => { test('should validate correct parameters', () => { const params = { input: 'test input', options: { format: 'json', verbose: true } }; expect(tool.validate(params)).toBe(true); }); test('should reject invalid parameters', () => { const params = { input: 123, // Should be string options: { format: 'invalid' } }; expect(tool.validate(params)).toBe(false); }); }); describe('execution', () => { test('should execute successfully with valid params', async () => { const params = { input: 'test input', options: { format: 'json' } }; const result = await tool.execute(params); expect(result.success).toBe(true); expect(result.data).toBeDefined(); expect(result.metadata.toolName).toBe('custom_tool'); }); test('should handle errors gracefully', async () => { const params = { input: '', // Empty input might cause error options: { format: 'json' } }; await expect(tool.execute(params)).rejects.toThrow(); }); }); }); ``` ### Integration Test Structure ```typescript import { describe, test, expect, beforeAll, afterAll } from '@jest/globals'; import { MCPClient } from '@modelcontextprotocol/sdk/client/index.js'; import { spawn, ChildProcess } from 'child_process'; describe('Integration Tests', () => { let serverProcess: ChildProcess; let client: MCPClient; beforeAll(async () => { // Start MCP server serverProcess = spawn('node', ['dist/server.js']); // Wait for server to start await new Promise(resolve => setTimeout(resolve, 2000)); // Connect client client = new MCPClient({ name: 'test-client', version: '1.0.0' }); await client.connect({ command: 'node', args: ['dist/server.js'] }); }); afterAll(async () => { // Cleanup if (client) { await client.close(); } if (serverProcess) { serverProcess.kill(); } }); test('should list available tools', async () => { const tools = await client.listTools(); expect(tools).toHaveLength(6); // Core tools expect(tools.map(t => t.name)).toContain('listAgents'); expect(tools.map(t => t.name)).toContain('forceDelegation'); }); test('should delegate task successfully', async () => { const result = await client.call('forceDelegation', { task: 'Create a simple React component', targetAgent: 'frontend-developer', enforcementLevel: 'strict' }); expect(result.delegationEnforced).toBe(true); expect(result.agentUsed).toBe('frontend-developer'); }); }); ``` ### Performance Testing ```typescript import { describe, test, expect } from '@jest/globals'; import { performance } from 'perf_hooks'; describe('Performance Tests', () => { test('delegation should complete within reasonable time', async () => { const start = performance.now(); const result = await client.call('forceDelegation', { task: 'Simple task for performance testing', targetAgent: 'backend-architect' }); const duration = performance.now() - start; expect(result.success).toBe(true); expect(duration).toBeLessThan(5000); // 5 seconds max }); test('should handle concurrent delegations', async () => { const concurrentTasks = Array.from({ length: 10 }, (_, i) => client.call('forceDelegation', { task: `Concurrent task ${i}`, targetAgent: 'backend-architect' }) ); const start = performance.now(); const results = await Promise.all(concurrentTasks); const duration = performance.now() - start; expect(results.every(r => r.success)).toBe(true); expect(duration).toBeLessThan(15000); // 15 seconds for 10 concurrent tasks }); }); ``` ## Deployment and Distribution ### Building for Distribution ```typescript // scripts/build-distribution.ts import { build } from 'esbuild'; import { copyFile, mkdir } from 'fs/promises'; async function buildDistribution() { // Build main server await build({ entryPoints: ['src/server.ts'], bundle: true, platform: 'node', target: 'node18', outfile: 'dist/server.js', external: ['@modelcontextprotocol/sdk'] }); // Build CLI tools await build({ entryPoints: ['src/cli/bootstrap.ts'], bundle: true, platform: 'node', target: 'node18', outfile: 'dist/cli/bootstrap.js' }); // Copy necessary files await mkdir('dist/config', { recursive: true }); await copyFile('config/default.json', 'dist/config/default.json'); console.log('Distribution build completed'); } buildDistribution().catch(console.error); ``` ### Package Configuration ```json { "name": "claude-code-subagents-orchestrator", "version": "1.0.0", "main": "dist/server.js", "bin": { "claude-orchestrator": "dist/cli/bootstrap.js" }, "files": [ "dist/**/*", "config/**/*", "docs/**/*", "README.md", "LICENSE" ], "engines": { "node": ">=18.0.0" }, "scripts": { "prepublishOnly": "npm run build && npm test" } } ``` ## Contributing Guidelines ### Code Style Follow these coding standards: ```typescript // Use strict TypeScript interface StrictInterface { requiredProperty: string; optionalProperty?: number; } // Prefer async/await over promises async function goodAsyncFunction(): Promise<Result> { try { const result = await someAsyncOperation(); return { success: true, data: result }; } catch (error) { throw new OrchestratorError(ErrorCode.OPERATION_FAILED, error.message); } } // Use descriptive variable names const delegationResult = await forceDelegation(task, agent); const validationOutcome = await validateDelegation(request, response); // Prefer early returns function validateRequest(request: any): boolean { if (!request) return false; if (!request.task) return false; if (typeof request.task !== 'string') return false; return true; } ``` ### Pull Request Process 1. **Fork and Branch**: Create a feature branch from `main` 2. **Implement**: Add your changes with tests 3. **Test**: Ensure all tests pass and coverage is maintained 4. **Document**: Update documentation for any API changes 5. **Submit**: Create PR with detailed description ### Commit Messages Use conventional commit format: ``` feat: add custom delegation rules support fix: resolve agent spawn timeout issues docs: update developer guide with plugin system test: add integration tests for custom tools refactor: simplify task classification logic ``` ## Advanced Topics ### Custom Prompt Engineering Implement custom prompt generation strategies: ```typescript export class CustomPromptStrategy implements IPromptStrategy { generatePrompt( task: string, context: DelegationContext, agentSpec: AgentSpec ): PromptResult { const customPrompt = ` # Custom Specialized Task ## Context You are ${agentSpec.metadata.name}, a specialist in ${agentSpec.category}. ## Task ${task} ## Specialized Approach ${this.generateSpecializedApproach(agentSpec, context)} ## Validation Requirements ${this.generateValidationRequirements(context)} ## Output Format ${this.generateOutputFormat(context)} `; return { prompt: customPrompt, metadata: { strategy: 'custom', agentType: agentSpec.metadata.name, complexity: context.detectedIntent.complexity } }; } } ``` ### Performance Monitoring Implement custom metrics collection: ```typescript export class CustomMetricsCollector { private metrics: Map<string, MetricData> = new Map(); recordDelegation(agentId: string, duration: number, success: boolean) { const key = `delegation.${agentId}`; const existing = this.metrics.get(key) || { count: 0, totalDuration: 0, successCount: 0, lastUpdated: new Date() }; this.metrics.set(key, { count: existing.count + 1, totalDuration: existing.totalDuration + duration, successCount: existing.successCount + (success ? 1 : 0), lastUpdated: new Date() }); } getMetrics(): MetricsReport { const report: MetricsReport = { delegations: {}, summary: { totalDelegations: 0, averageDuration: 0, successRate: 0 } }; for (const [key, data] of this.metrics) { if (key.startsWith('delegation.')) { const agentId = key.replace('delegation.', ''); report.delegations[agentId] = { count: data.count, averageDuration: data.totalDuration / data.count, successRate: data.successCount / data.count }; report.summary.totalDelegations += data.count; report.summary.averageDuration += data.totalDuration; report.summary.successRate += data.successCount; } } if (report.summary.totalDelegations > 0) { report.summary.averageDuration /= report.summary.totalDelegations; report.summary.successRate /= report.summary.totalDelegations; } return report; } } ``` This developer guide provides the foundation for extending and customizing the Claude Code Subagents Orchestrator. The modular architecture and well-defined interfaces make it easy to add new functionality while maintaining system reliability and performance.