UNPKG

@flexabrain/mcp-server

Version:

Advanced electrical schematic analysis MCP server with rail engineering expertise

944 lines (767 loc) โ€ข 27.3 kB
# ๐Ÿ› ๏ธ FlexaBrain MCP Server - Developer Guide **Extending and Contributing to FlexaBrain** This guide provides comprehensive information for developers who want to contribute to FlexaBrain or extend its capabilities with custom functionality. --- ## ๐Ÿ—๏ธ **Development Environment Setup** ### Prerequisites ```bash # Required software - Node.js 18+ with npm/yarn - Git - VS Code (recommended) with extensions: - TypeScript and JavaScript Language Features - ESLint - Prettier - Jest Test Explorer # Optional but recommended - Docker for containerized development - Tesseract OCR for local testing ``` ### Initial Setup ```bash # Clone and setup the repository git clone https://github.com/your-org/flexabrain-mcp.git cd flexabrain-mcp # Install dependencies npm install # Setup development environment npm run setup-dev # Run tests to verify setup npm test # Start development server with hot reload npm run dev ``` ### Development Scripts ```bash # Development npm run dev # Start with hot reload npm run build # Build production version npm run build:watch # Build with file watching # Testing npm test # Run all tests npm run test:watch # Run tests in watch mode npm run test:coverage # Generate coverage report npm run test:services # Run service tests only npm run test:tools # Run MCP tool tests only # Code Quality npm run lint # Run ESLint npm run lint:fix # Fix linting issues npm run format # Run Prettier formatting npm run type-check # TypeScript type checking # Documentation npm run docs:serve # Serve documentation locally npm run docs:build # Build documentation site ``` --- ## ๐Ÿงฉ **Architecture Deep Dive** ### Core Architecture Principles 1. **Modular Design**: Each service is independently testable and replaceable 2. **Type Safety**: Comprehensive TypeScript typing throughout 3. **Domain Expertise**: Rail engineering knowledge embedded in code and data 4. **Error Resilience**: Graceful degradation and comprehensive error handling 5. **Performance**: Optimized for real-world schematic analysis workflows ### Directory Structure ``` flexabrain-mcp/ โ”œโ”€โ”€ src/ # Source code โ”‚ โ”œโ”€โ”€ services/ # Core analysis services โ”‚ โ”‚ โ”œโ”€โ”€ ocr-service.ts # Tesseract.js OCR integration โ”‚ โ”‚ โ”œโ”€โ”€ component-classifier.ts # ML-powered component recognition โ”‚ โ”‚ โ””โ”€โ”€ electrical-analyzer.ts # Main analysis orchestrator โ”‚ โ”œโ”€โ”€ tools/ # MCP tool implementations โ”‚ โ”‚ โ”œโ”€โ”€ analyze-schematic.ts # Primary analysis tool โ”‚ โ”‚ โ”œโ”€โ”€ extract-component-inventory.ts โ”‚ โ”‚ โ””โ”€โ”€ [6 more specialized tools] โ”‚ โ”œโ”€โ”€ types/ # TypeScript type definitions โ”‚ โ”‚ โ”œโ”€โ”€ electrical.ts # Electrical component types โ”‚ โ”‚ โ”œโ”€โ”€ schematic.ts # Analysis result types โ”‚ โ”‚ โ””โ”€โ”€ mcp.ts # MCP protocol types โ”‚ โ”œโ”€โ”€ data/ # Knowledge base and patterns โ”‚ โ”‚ โ””โ”€โ”€ rail-components.ts # Rail component database โ”‚ โ””โ”€โ”€ index.ts # MCP server entry point โ”œโ”€โ”€ tests/ # Test suites โ”‚ โ”œโ”€โ”€ setup.ts # Test utilities and mocks โ”‚ โ”œโ”€โ”€ services/ # Service unit tests โ”‚ โ””โ”€โ”€ tools/ # MCP tool integration tests โ”œโ”€โ”€ docs/ # Documentation โ””โ”€โ”€ examples/ # Usage examples and samples ``` ### Service Dependencies ```mermaid graph TB A[MCP Server] --> B[MCP Tools] B --> C[Electrical Analyzer] C --> D[OCR Service] C --> E[Component Classifier] C --> F[Safety Analysis] C --> G[Standards Validator] D --> H[Tesseract.js] E --> I[Rail Components DB] F --> J[IEEE 1584 Engine] G --> K[Standards Database] ``` --- ## ๐Ÿ”ง **Extending FlexaBrain** ### Adding New MCP Tools #### 1. Create Tool Implementation ```typescript // src/tools/my-custom-tool.ts import { MyCustomArgs, MCPToolResponse } from '../types/mcp.js'; import { electricalSchematicAnalyzer } from '../services/electrical-analyzer.js'; /** * Custom MCP tool for specialized electrical analysis */ export async function myCustomTool(args: MyCustomArgs): Promise<MCPToolResponse> { try { // Validate input parameters validateCustomArgs(args); // Perform analysis using existing services const analysisResult = await electricalSchematicAnalyzer.analyzeSchematic( args.image_path, { analysis_depth: args.depth || 'standard', focus_areas: ['safety'] // Custom focus } ); if (!analysisResult.success || !analysisResult.analysis) { return formatErrorResponse('Analysis failed'); } // Custom processing logic const customResults = performCustomAnalysis(analysisResult.analysis); // Format response return { content: [{ type: 'text', text: formatCustomReport(customResults) }] }; } catch (error) { return formatErrorResponse(error instanceof Error ? error.message : String(error)); } } function validateCustomArgs(args: MyCustomArgs): void { if (!args.image_path) { throw new Error('image_path is required'); } // Additional validation... } function performCustomAnalysis(analysis: SchematicAnalysis): CustomResults { // Your custom analysis logic here return { custom_metric: calculateCustomMetric(analysis.components), recommendations: generateCustomRecommendations(analysis) }; } ``` #### 2. Add Type Definitions ```typescript // src/types/mcp.ts - Add to existing types export interface MyCustomArgs { image_path: string; depth?: 'basic' | 'standard' | 'comprehensive'; custom_parameter?: string; } export interface CustomResults { custom_metric: number; recommendations: string[]; } ``` #### 3. Register Tool with MCP Server ```typescript // src/index.ts - Add to tool registration import { myCustomTool } from './tools/my-custom-tool.js'; const server = new Server( { name: 'flexabrain-mcp', version: '1.0.0' }, { capabilities: { tools: {} } } ); // Register your custom tool server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // ... existing tools { name: 'my_custom_tool', description: 'Custom electrical analysis tool', inputSchema: { type: 'object', properties: { image_path: { type: 'string', description: 'Path to schematic image' }, depth: { type: 'string', enum: ['basic', 'standard', 'comprehensive'], description: 'Analysis depth' }, custom_parameter: { type: 'string', description: 'Custom parameter' } }, required: ['image_path'] } } ] }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { // ... existing tools case 'my_custom_tool': return await myCustomTool(args as MyCustomArgs); default: throw new Error(`Unknown tool: ${name}`); } }); ``` ### Adding New Component Types #### 1. Extend Type Definitions ```typescript // src/types/electrical.ts - Add to ComponentType enum export enum ComponentType { // ... existing types CUSTOM_COMPONENT = 'custom_component', SPECIALIZED_RELAY = 'specialized_relay' } export enum ComponentCategory { // ... existing categories CUSTOM_CATEGORY = 'custom_category' } ``` #### 2. Add Recognition Patterns ```typescript // src/data/rail-components.ts - Add to RAIL_COMPONENT_PATTERNS export const RAIL_COMPONENT_PATTERNS: Record<ComponentType, ComponentPattern> = { // ... existing patterns [ComponentType.CUSTOM_COMPONENT]: { patterns: [ /^CUSTOM\d{3,4}[A-Z]?$/, // CUSTOM001, CUSTOM1234A /^CC\d{2,3}[A-Z]?$/ // CC01, CC123B ], prefixes: ['CUSTOM', 'CC'], description: 'Custom specialized component for rail applications', confidence_boost: 0.15, context_clues: ['custom', 'specialized', 'rail-specific'] } }; // Add specifications export const RAIL_COMPONENT_SPECIFICATIONS: Record<ComponentType, RailComponentSpec> = { // ... existing specifications [ComponentType.CUSTOM_COMPONENT]: { voltage_rating: { min: 0, max: 1500, unit: 'V_DC' }, current_rating: { min: 0, max: 100, unit: 'A' }, standards_compliance: ['Custom-Standard-001', 'Rail-Spec-Custom'], safety_requirements: [ { type: 'ELECTRICAL_SHOCK', voltage_class: 'LV', ppe_required: true } ], maintenance_intervals: { inspection: { frequency: 6, unit: 'months' }, testing: { frequency: 12, unit: 'months' } }, typical_applications: ['Specialized rail control', 'Custom protection'] } }; ``` #### 3. Update Component Classifier ```typescript // src/services/component-classifier.ts - Update category mapping private determineComponentCategory(type: ComponentType): ComponentCategory { const categoryMap: Record<ComponentType, ComponentCategory> = { // ... existing mappings [ComponentType.CUSTOM_COMPONENT]: ComponentCategory.CUSTOM_CATEGORY, [ComponentType.SPECIALIZED_RELAY]: ComponentCategory.CONTROL }; return categoryMap[type] || ComponentCategory.UNCLASSIFIED; } ``` ### Adding New Standards #### 1. Create Standards Validator ```typescript // src/services/standards/custom-standard-validator.ts import { ElectricalComponent } from '../../types/electrical.js'; export interface CustomStandardResult { compliant: boolean; violations: string[]; recommendations: string[]; } export class CustomStandardValidator { async validateCustomStandard001(components: ElectricalComponent[]): Promise<CustomStandardResult> { const violations = []; const recommendations = []; // Example validation rule const highVoltageComponents = components.filter(c => c.specifications?.voltage_rating && c.specifications.voltage_rating.max > 1000 ); for (const component of highVoltageComponents) { // Check for required surge protection if (!this.hasSurgeProtection(component)) { violations.push(`${component.id}: Missing surge protection for >1kV component`); recommendations.push(`Install surge arresters for component ${component.id}`); } } return { compliant: violations.length === 0, violations, recommendations }; } private hasSurgeProtection(component: ElectricalComponent): boolean { // Implementation logic to check for surge protection return component.specifications?.safety_requirements?.some(req => req.type === 'SURGE_PROTECTION' ) ?? false; } } ``` #### 2. Integrate with Standards Validation ```typescript // src/services/electrical-analyzer.ts - Update generateComplianceReport import { CustomStandardValidator } from './standards/custom-standard-validator.js'; private async generateComplianceReport( components: ElectricalComponent[], options: AnalysisOptions ): Promise<ComplianceReport> { // ... existing code // Add custom standard validation if (options.custom_standards?.includes('CUSTOM-STANDARD-001')) { const customValidator = new CustomStandardValidator(); const customResult = await customValidator.validateCustomStandard001(components); if (!customResult.compliant) { nonCompliantItems.push(...customResult.violations.map(violation => ({ standard: 'CUSTOM-STANDARD-001', item: violation.split(':')[0], status: 'NON_COMPLIANT' as const, violation: violation.split(':')[1], recommendation: customResult.recommendations[0] || 'Review compliance', priority: RecommendationPriority.HIGH }))); } } // ... rest of function } ``` --- ## ๐Ÿงช **Testing Guidelines** ### Test Structure FlexaBrain uses a comprehensive testing approach: 1. **Unit Tests**: Individual service and function testing 2. **Integration Tests**: MCP tool and workflow testing 3. **End-to-End Tests**: Complete analysis pipeline testing 4. **Performance Tests**: Load and benchmark testing ### Writing Service Tests ```typescript // tests/services/my-service.test.ts import { describe, test, expect, beforeEach, jest } from '@jest/globals'; import { MyService } from '../../src/services/my-service.js'; import { TestUtils, MockDataGenerator } from '../setup.js'; describe('My Service', () => { let service: MyService; beforeEach(() => { service = new MyService(); jest.clearAllMocks(); }); describe('Core Functionality', () => { test('should process input correctly', async () => { // Arrange const mockInput = MockDataGenerator.generateMockInput(); const expectedOutput = TestUtils.createExpectedOutput(); // Act const result = await service.processInput(mockInput); // Assert expect(result).toEqual(expectedOutput); expect(result.confidence).toBeGreaterThan(0.8); }); test('should handle edge cases gracefully', async () => { // Test edge cases const emptyInput = {}; const result = await service.processInput(emptyInput); expect(result).toBeDefined(); expect(result.errors).toBeDefined(); }); }); describe('Error Handling', () => { test('should handle service failures', async () => { // Mock dependency failure jest.spyOn(service, 'dependencyMethod').mockRejectedValue( new Error('Dependency failed') ); await expect(service.processInput({})).rejects.toThrow('Dependency failed'); }); }); describe('Performance', () => { test('should complete within time limits', async () => { const largeInput = MockDataGenerator.generateLargeInput(); const { result, duration } = await TestUtils.measureExecutionTime(async () => { return await service.processInput(largeInput); }); expect(result).toBeDefined(); expect(duration).toBeLessThan(5000); // 5 second limit }); }); }); ``` ### Writing MCP Tool Tests ```typescript // tests/tools/my-tool.test.ts import { describe, test, expect, beforeEach, jest } from '@jest/globals'; import { myCustomTool } from '../../src/tools/my-custom-tool.js'; import { electricalSchematicAnalyzer } from '../../src/services/electrical-analyzer.js'; // Mock dependencies jest.mock('../../src/services/electrical-analyzer.js'); describe('My Custom Tool', () => { beforeEach(() => { jest.clearAllMocks(); }); test('should return proper MCP response structure', async () => { // Mock analyzer response const mockAnalysis = { success: true, analysis: { components: [/* mock components */], // ... other analysis data } }; jest.mocked(electricalSchematicAnalyzer.analyzeSchematic).mockResolvedValue(mockAnalysis); const args = { image_path: 'test-schematic.png', custom_parameter: 'test-value' }; const result = await myCustomTool(args); // Verify MCP response structure expect(result).toHaveProperty('content'); expect(Array.isArray(result.content)).toBe(true); expect(result.content[0]).toHaveProperty('type', 'text'); expect(result.content[0]).toHaveProperty('text'); }); test('should validate input parameters', async () => { const invalidArgs = { custom_parameter: 'test-value' // Missing required image_path }; const result = await myCustomTool(invalidArgs); expect(result.content[0].text).toContain('image_path is required'); }); }); ``` ### Mock Data Generation ```typescript // tests/setup.ts - Add to MockDataGenerator export class MockDataGenerator { // ... existing methods static generateCustomMockData(): CustomMockData { return { custom_field: 'mock-value', numeric_field: Math.random() * 100, array_field: ['item1', 'item2', 'item3'], nested_object: { sub_field: 'nested-value' } }; } static generateMockAnalysisWithCustomData(): AnalysisResult { return { success: true, analysis: { // ... standard analysis fields custom_results: this.generateCustomMockData() }, processing_stats: { total_time: 2500, ocr_time: 1000, analysis_time: 1500, validation_time: 0 } }; } } ``` --- ## ๐Ÿ“Š **Performance Optimization** ### Profiling and Monitoring ```typescript // Performance monitoring utilities export class PerformanceMonitor { private static metrics: Map<string, number[]> = new Map(); static startTimer(operation: string): () => number { const startTime = performance.now(); return () => { const duration = performance.now() - startTime; this.recordMetric(operation, duration); return duration; }; } static recordMetric(operation: string, duration: number): void { if (!this.metrics.has(operation)) { this.metrics.set(operation, []); } this.metrics.get(operation)!.push(duration); } static getMetrics(): Record<string, { avg: number; min: number; max: number; count: number }> { const result: Record<string, any> = {}; for (const [operation, durations] of this.metrics) { result[operation] = { avg: durations.reduce((a, b) => a + b, 0) / durations.length, min: Math.min(...durations), max: Math.max(...durations), count: durations.length }; } return result; } } // Usage in services export class OptimizedService { async processWithMonitoring(input: any): Promise<any> { const endTimer = PerformanceMonitor.startTimer('processWithMonitoring'); try { // Your processing logic const result = await this.actualProcessing(input); return result; } finally { const duration = endTimer(); console.log(`Processing completed in ${duration}ms`); } } } ``` ### Memory Management ```typescript // Memory-efficient large file processing export class MemoryEfficientProcessor { private readonly MAX_CONCURRENT = 4; private readonly MEMORY_THRESHOLD = 512 * 1024 * 1024; // 512MB async processBatch(filePaths: string[]): Promise<any[]> { const results = []; const chunks = this.chunkArray(filePaths, this.MAX_CONCURRENT); for (const chunk of chunks) { // Monitor memory usage const memBefore = process.memoryUsage().heapUsed; // Process chunk in parallel const chunkResults = await Promise.all( chunk.map(path => this.processSingleFile(path)) ); results.push(...chunkResults); // Force garbage collection if memory usage is high const memAfter = process.memoryUsage().heapUsed; if (memAfter > this.MEMORY_THRESHOLD) { if (global.gc) { global.gc(); } } } return results; } private chunkArray<T>(array: T[], chunkSize: number): T[][] { const chunks = []; for (let i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)); } return chunks; } } ``` --- ## ๐Ÿ”’ **Security Considerations** ### Input Validation ```typescript // Comprehensive input validation export class InputValidator { static validateImagePath(path: string): void { if (!path || typeof path !== 'string') { throw new Error('Invalid image path'); } // Check for path traversal attempts if (path.includes('../') || path.includes('..\\')) { throw new Error('Path traversal not allowed'); } // Validate file extension const allowedExtensions = ['.png', '.jpg', '.jpeg', '.tiff', '.pdf']; const extension = path.toLowerCase().substring(path.lastIndexOf('.')); if (!allowedExtensions.includes(extension)) { throw new Error(`Unsupported file extension: ${extension}`); } } static validateAnalysisOptions(options: any): void { if (options.analysis_depth && !['basic', 'standard', 'comprehensive'].includes(options.analysis_depth)) { throw new Error('Invalid analysis depth'); } if (options.focus_areas && !Array.isArray(options.focus_areas)) { throw new Error('Focus areas must be an array'); } // Validate each focus area const validFocusAreas = ['safety', 'compliance', 'performance', 'maintenance']; if (options.focus_areas?.some((area: string) => !validFocusAreas.includes(area))) { throw new Error('Invalid focus area specified'); } } } ``` ### Error Sanitization ```typescript // Sanitize errors before returning to client export class ErrorSanitizer { static sanitizeError(error: Error): { message: string; code?: string } { // Remove sensitive information from error messages let message = error.message; // Remove file system paths message = message.replace(/\/[^\s]+/g, '[PATH_REMOVED]'); message = message.replace(/[A-Z]:\\.+/g, '[PATH_REMOVED]'); // Remove potential database connection strings message = message.replace(/mongodb:\/\/[^\s]+/g, '[CONNECTION_REMOVED]'); message = message.replace(/postgresql:\/\/[^\s]+/g, '[CONNECTION_REMOVED]'); return { message, code: error.name || 'UNKNOWN_ERROR' }; } } ``` --- ## ๐Ÿ“ฆ **Building and Deployment** ### Build Configuration ```typescript // build.config.ts export const buildConfig = { // TypeScript compilation options typescript: { target: 'ES2020', module: 'ESNext', moduleResolution: 'node', strict: true, skipLibCheck: true, forceConsistentCasingInFileNames: true }, // Bundle optimization bundle: { minify: process.env.NODE_ENV === 'production', sourceMaps: process.env.NODE_ENV !== 'production', treeShaking: true }, // Asset handling assets: { copyStaticFiles: ['data/', 'docs/'], excludePatterns: ['*.test.ts', '*.spec.ts'] } }; ``` ### Docker Development ```dockerfile # Dockerfile.dev - Development container FROM node:18-alpine WORKDIR /app # Install system dependencies RUN apk add --no-cache \ tesseract-ocr \ tesseract-ocr-data-eng # Copy package files COPY package*.json ./ RUN npm ci # Copy source code COPY . . # Development command CMD ["npm", "run", "dev"] ``` ### Environment Configuration ```typescript // src/config/environment.ts export interface EnvironmentConfig { nodeEnv: 'development' | 'production' | 'test'; logLevel: 'error' | 'warn' | 'info' | 'debug'; tesseractConfig: { language: string; oem: number; psm: number; }; performance: { maxConcurrentAnalyses: number; timeoutMs: number; memoryLimitMB: number; }; } export const getEnvironmentConfig = (): EnvironmentConfig => { return { nodeEnv: (process.env.NODE_ENV as any) || 'development', logLevel: (process.env.LOG_LEVEL as any) || 'info', tesseractConfig: { language: process.env.TESSERACT_LANG || 'eng', oem: parseInt(process.env.TESSERACT_OEM || '1'), psm: parseInt(process.env.TESSERACT_PSM || '6') }, performance: { maxConcurrentAnalyses: parseInt(process.env.MAX_CONCURRENT || '4'), timeoutMs: parseInt(process.env.TIMEOUT_MS || '30000'), memoryLimitMB: parseInt(process.env.MEMORY_LIMIT_MB || '512') } }; }; ``` --- ## ๐Ÿ“š **Contributing Guidelines** ### Code Style - **TypeScript**: Use strict mode with comprehensive typing - **ESLint**: Follow the project's ESLint configuration - **Prettier**: Consistent code formatting - **Naming**: Descriptive names, camelCase for variables/functions, PascalCase for classes - **Comments**: JSDoc for public APIs, inline comments for complex logic ### Pull Request Process 1. **Fork and Branch**: Create feature branch from `main` 2. **Implementation**: Implement feature with comprehensive tests 3. **Testing**: Ensure all tests pass and coverage requirements met 4. **Documentation**: Update relevant documentation 5. **Review**: Submit PR with clear description and examples ### Commit Guidelines ```bash # Format: type(scope): description feat(ocr): add support for PDF schematic analysis fix(classifier): improve component confidence scoring docs(api): update MCP tool parameter documentation test(services): add integration tests for batch processing refactor(analyzer): optimize memory usage in large file processing ``` ### Adding Examples ```typescript // examples/custom-analysis-example.ts import { analyzeElectricalSchematic } from '../src/tools/analyze-schematic.js'; /** * Example: Custom analysis workflow for high-speed rail schematics */ async function customHighSpeedRailAnalysis() { try { const result = await analyzeElectricalSchematic({ image_path: './examples/schematics/high-speed-rail-25kv.png', analysis_depth: 'comprehensive', focus_areas: ['safety', 'compliance', 'performance'], rail_system_context: { system_type: 'HIGH_SPEED', voltage_system: '25kV AC 50Hz', region: 'EU', applicable_standards: ['TSI', 'EN 50388', 'IEC 62888'] } }); console.log('Analysis completed successfully'); console.log('Safety Level:', extractSafetyLevel(result.content[0].text)); console.log('Compliance Score:', extractComplianceScore(result.content[0].text)); } catch (error) { console.error('Analysis failed:', error); } } // Helper functions for parsing results function extractSafetyLevel(text: string): string { const match = text.match(/Priority Level: ([A-Z]+)/); return match ? match[1] : 'UNKNOWN'; } function extractComplianceScore(text: string): string { const match = text.match(/Overall: ([\d.]+)%/); return match ? match[1] + '%' : 'N/A'; } // Run example if (require.main === module) { customHighSpeedRailAnalysis(); } ``` --- This developer guide provides comprehensive information for extending FlexaBrain's capabilities. For additional support, see the [API Reference](API.md) and join our [GitHub Discussions](https://github.com/your-org/flexabrain-mcp/discussions).