@flexabrain/mcp-server
Version:
Advanced electrical schematic analysis MCP server with rail engineering expertise
944 lines (767 loc) โข 27.3 kB
Markdown
# ๐ ๏ธ 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).