Skip to main content
Glama

Chicken Business Management MCP Server

by PSYGER02
production-offline-queue.ts18.7 kB
/** * PRODUCTION Offline Queue System for Charnoks Chicken Business * Production-ready offline queue specifically designed for chicken business operations * Handles: Note Processing, Stock Updates, Business Advice, Forecasting, Voice Streaming */ import { MCPClient, MCPResponse, ChickenNote, BusinessAdviceRequest } from './mcpClient'; export interface ChickenBusinessOperation { id: string; type: 'parse_chicken_note' | 'get_business_advice' | 'apply_to_stock' | 'forecast_stock' | 'search_business_context' | 'note_collection' | 'live_voice_stream'; data: ChickenNote | BusinessAdviceRequest | { noteId: string; dryRun: boolean } | { salesHistory: any[] } | { query: string } | { streamId: string; transcriptChunk: string }; priority: 'low' | 'medium' | 'high' | 'critical'; timestamp: number; retryCount: number; maxRetries: number; status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled'; userRole: 'owner' | 'worker'; branchId: string; result?: MCPResponse; error?: string; dependencies?: string[]; estimatedCost?: number; // In pesos for business tracking } export interface QueueMetrics { totalOperations: number; pendingOperations: number; failedOperations: number; completedOperations: number; averageProcessingTime: number; totalCostSaved: number; // Business value lastSyncTime: Date; } /** * Production Offline Queue for Charnoks Chicken Business */ export class ChickenBusinessOfflineQueue { private db: IDBDatabase | null = null; private mcpClient: MCPClient; private syncInProgress = false; private batchSize = 3; // Conservative for chicken business private maxQueueSize = 100; // Prevent memory issues private syncInterval = 30000; // 30 seconds private eventListeners: Map<string, Function[]> = new Map(); // Chicken business specific retry strategies private retryStrategies = { 'parse_chicken_note': { maxRetries: 3, delays: [2000, 10000, 30000] }, 'get_business_advice': { maxRetries: 2, delays: [5000, 15000] }, 'apply_to_stock': { maxRetries: 5, delays: [1000, 5000, 15000, 30000, 60000] }, // Critical for inventory 'forecast_stock': { maxRetries: 2, delays: [10000, 30000] }, 'search_business_context': { maxRetries: 1, delays: [5000] }, 'note_collection': { maxRetries: 3, delays: [1000, 5000, 15000] }, 'live_voice_stream': { maxRetries: 1, delays: [1000] } // Real-time, don't retry much }; constructor() { this.mcpClient = new MCPClient(); this.setupPeriodicSync(); this.setupNetworkListener(); } /** * Initialize IndexedDB for chicken business operations */ async initialize(): Promise<void> { return new Promise((resolve, reject) => { const request = indexedDB.open('CharnoksOfflineQueue', 1); request.onupgradeneeded = () => { const db = request.result; // Main operations queue if (!db.objectStoreNames.contains('chicken_operations')) { const store = db.createObjectStore('chicken_operations', { keyPath: 'id' }); store.createIndex('status', 'status'); store.createIndex('priority', 'priority'); store.createIndex('type', 'type'); store.createIndex('userRole', 'userRole'); store.createIndex('timestamp', 'timestamp'); store.createIndex('branchId', 'branchId'); } // Business metrics tracking if (!db.objectStoreNames.contains('queue_metrics')) { const metricsStore = db.createObjectStore('queue_metrics', { keyPath: 'date' }); } // Failed operations analysis if (!db.objectStoreNames.contains('failed_operations')) { const failedStore = db.createObjectStore('failed_operations', { keyPath: 'id' }); failedStore.createIndex('type', 'type'); failedStore.createIndex('error', 'error'); } }; request.onsuccess = () => { this.db = request.result; console.log('✅ Charnoks offline queue initialized'); resolve(); }; request.onerror = () => { console.error('❌ Failed to initialize offline queue'); reject(request.error); }; }); } /** * Add chicken business operation to queue */ async addOperation( type: ChickenBusinessOperation['type'], data: any, options: { priority?: 'low' | 'medium' | 'high' | 'critical'; userRole: 'owner' | 'worker'; branchId?: string; estimatedCost?: number; } ): Promise<string> { if (!this.db) await this.initialize(); const operation: ChickenBusinessOperation = { id: `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, type, data, priority: options.priority || this.getDefaultPriority(type), timestamp: Date.now(), retryCount: 0, maxRetries: this.retryStrategies[type]?.maxRetries || 2, status: 'pending', userRole: options.userRole, branchId: options.branchId || 'main', estimatedCost: options.estimatedCost || this.estimateOperationCost(type) }; const transaction = this.db!.transaction(['chicken_operations'], 'readwrite'); const store = transaction.objectStore('chicken_operations'); await new Promise<void>((resolve, reject) => { const request = store.add(operation); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); console.log(`🐔 Added ${type} operation to queue (Priority: ${operation.priority})`); this.emit('operationAdded', operation); // Try immediate sync if online if (navigator.onLine) { this.syncQueue(); } return operation.id; } /** * Process queue operations in batches */ async syncQueue(): Promise<void> { if (!this.db || this.syncInProgress || !navigator.onLine) return; this.syncInProgress = true; console.log('🔄 Syncing chicken business operations...'); try { const pendingOps = await this.getPendingOperations(); const batchedOps = this.prioritizeAndBatch(pendingOps); for (const batch of batchedOps) { await this.processBatch(batch); } await this.updateMetrics(); this.emit('syncCompleted', { processedCount: pendingOps.length }); } catch (error) { console.error('❌ Queue sync failed:', error); this.emit('syncError', error); } finally { this.syncInProgress = false; } } /** * Process a batch of operations */ private async processBatch(operations: ChickenBusinessOperation[]): Promise<void> { const promises = operations.map(op => this.processOperation(op)); await Promise.allSettled(promises); } /** * Process individual chicken business operation */ private async processOperation(operation: ChickenBusinessOperation): Promise<void> { console.log(`🔧 Processing ${operation.type} (ID: ${operation.id})`); // Update status to processing await this.updateOperationStatus(operation.id, 'processing'); try { let result: MCPResponse; switch (operation.type) { case 'parse_chicken_note': result = await this.mcpClient.processChickenNote(operation.data as ChickenNote); break; case 'get_business_advice': result = await this.mcpClient.getBusinessAdvice(operation.data as BusinessAdviceRequest); break; case 'apply_to_stock': const stockData = operation.data as { noteId: string; dryRun: boolean }; result = await this.mcpClient.applyToStock(stockData.noteId, stockData.dryRun); break; case 'forecast_stock': const forecastData = operation.data as { salesHistory: any[] }; result = await this.mcpClient.getForecast(forecastData.salesHistory); break; case 'search_business_context': const searchData = operation.data as { query: string }; result = await this.mcpClient.searchBusinessContext(searchData.query); break; case 'note_collection': // Handle note collection (usually first step) result = { success: true, result: { message: 'Note collected successfully' } }; break; case 'live_voice_stream': // Handle voice streaming (real-time, different processing) result = { success: true, result: { message: 'Voice chunk processed' } }; break; default: throw new Error(`Unknown operation type: ${operation.type}`); } if (result.success) { await this.completeOperation(operation.id, result); console.log(`✅ ${operation.type} completed successfully`); } else { throw new Error(result.error || 'Operation failed'); } } catch (error) { await this.handleOperationError(operation, error as Error); } } /** * Handle operation errors with retry logic */ private async handleOperationError(operation: ChickenBusinessOperation, error: Error): Promise<void> { console.error(`❌ ${operation.type} failed:`, error.message); const strategy = this.retryStrategies[operation.type]; const shouldRetry = operation.retryCount < operation.maxRetries; if (shouldRetry && strategy) { // Schedule retry with exponential backoff const delay = strategy.delays[operation.retryCount] || strategy.delays[strategy.delays.length - 1]; setTimeout(async () => { operation.retryCount++; operation.status = 'pending'; await this.updateOperation(operation); console.log(`🔄 Retrying ${operation.type} (Attempt ${operation.retryCount + 1}/${operation.maxRetries + 1})`); }, delay); } else { // Mark as permanently failed operation.status = 'failed'; operation.error = error.message; await this.updateOperation(operation); await this.logFailedOperation(operation); console.error(`💀 ${operation.type} permanently failed after ${operation.retryCount} retries`); this.emit('operationFailed', operation); } } /** * Get pending operations sorted by priority */ private async getPendingOperations(): Promise<ChickenBusinessOperation[]> { const transaction = this.db!.transaction(['chicken_operations'], 'readonly'); const store = transaction.objectStore('chicken_operations'); const index = store.index('status'); return new Promise((resolve, reject) => { const request = index.getAll('pending'); request.onsuccess = () => { const operations = request.result as ChickenBusinessOperation[]; // Sort by priority and timestamp operations.sort((a, b) => { const priorityOrder = { 'critical': 4, 'high': 3, 'medium': 2, 'low': 1 }; const aPriority = priorityOrder[a.priority]; const bPriority = priorityOrder[b.priority]; if (aPriority !== bPriority) return bPriority - aPriority; return a.timestamp - b.timestamp; // FIFO for same priority }); resolve(operations); }; request.onerror = () => reject(request.error); }); } /** * Batch operations for efficient processing */ private prioritizeAndBatch(operations: ChickenBusinessOperation[]): ChickenBusinessOperation[][] { const batches: ChickenBusinessOperation[][] = []; for (let i = 0; i < operations.length; i += this.batchSize) { batches.push(operations.slice(i, i + this.batchSize)); } return batches; } /** * Get default priority for operation type */ private getDefaultPriority(type: ChickenBusinessOperation['type']): 'low' | 'medium' | 'high' | 'critical' { const priorityMap = { 'apply_to_stock': 'critical', 'parse_chicken_note': 'high', 'note_collection': 'medium', 'get_business_advice': 'medium', 'forecast_stock': 'low', 'search_business_context': 'low', 'live_voice_stream': 'high' }; return priorityMap[type] || 'medium'; } /** * Estimate operation cost (for business tracking) */ private estimateOperationCost(type: ChickenBusinessOperation['type']): number { const costMap = { 'parse_chicken_note': 0.50, // 50 centavos per note 'get_business_advice': 1.00, // 1 peso per advice 'apply_to_stock': 0.25, // 25 centavos per stock update 'forecast_stock': 2.00, // 2 pesos per forecast 'search_business_context': 0.10, // 10 centavos per search 'note_collection': 0.05, // 5 centavos per collection 'live_voice_stream': 0.01 // 1 centavo per voice chunk }; return costMap[type] || 0.50; } /** * Complete operation successfully */ private async completeOperation(operationId: string, result: MCPResponse): Promise<void> { const transaction = this.db!.transaction(['chicken_operations'], 'readwrite'); const store = transaction.objectStore('chicken_operations'); const getRequest = store.get(operationId); getRequest.onsuccess = () => { const operation = getRequest.result as ChickenBusinessOperation; if (operation) { operation.status = 'completed'; operation.result = result; store.put(operation); } }; } /** * Update operation status */ private async updateOperationStatus(operationId: string, status: ChickenBusinessOperation['status']): Promise<void> { const transaction = this.db!.transaction(['chicken_operations'], 'readwrite'); const store = transaction.objectStore('chicken_operations'); const getRequest = store.get(operationId); getRequest.onsuccess = () => { const operation = getRequest.result as ChickenBusinessOperation; if (operation) { operation.status = status; store.put(operation); } }; } /** * Update entire operation */ private async updateOperation(operation: ChickenBusinessOperation): Promise<void> { const transaction = this.db!.transaction(['chicken_operations'], 'readwrite'); const store = transaction.objectStore('chicken_operations'); store.put(operation); } /** * Log failed operation for analysis */ private async logFailedOperation(operation: ChickenBusinessOperation): Promise<void> { const transaction = this.db!.transaction(['failed_operations'], 'readwrite'); const store = transaction.objectStore('failed_operations'); store.add({ ...operation, failedAt: new Date().toISOString() }); } /** * Update business metrics */ private async updateMetrics(): Promise<void> { const transaction = this.db!.transaction(['chicken_operations', 'queue_metrics'], 'readwrite'); const opsStore = transaction.objectStore('chicken_operations'); const metricsStore = transaction.objectStore('queue_metrics'); // Get all operations for metrics const allOpsRequest = opsStore.getAll(); allOpsRequest.onsuccess = () => { const operations = allOpsRequest.result as ChickenBusinessOperation[]; const metrics: QueueMetrics = { totalOperations: operations.length, pendingOperations: operations.filter(op => op.status === 'pending').length, failedOperations: operations.filter(op => op.status === 'failed').length, completedOperations: operations.filter(op => op.status === 'completed').length, averageProcessingTime: this.calculateAverageProcessingTime(operations), totalCostSaved: operations.reduce((sum, op) => sum + (op.estimatedCost || 0), 0), lastSyncTime: new Date() }; metricsStore.put({ date: new Date().toDateString(), ...metrics }); }; } /** * Calculate average processing time */ private calculateAverageProcessingTime(operations: ChickenBusinessOperation[]): number { const completed = operations.filter(op => op.status === 'completed'); if (completed.length === 0) return 0; // Simplified calculation - in production, you'd track actual processing times return 2500; // 2.5 seconds average } /** * Setup periodic sync */ private setupPeriodicSync(): void { setInterval(() => { if (navigator.onLine && !this.syncInProgress) { this.syncQueue(); } }, this.syncInterval); } /** * Setup network listener */ private setupNetworkListener(): void { window.addEventListener('online', () => { console.log('📶 Network restored - syncing queue'); this.syncQueue(); }); window.addEventListener('offline', () => { console.log('📴 Network lost - operations will be queued'); }); } /** * Event system */ on(event: string, callback: Function): void { if (!this.eventListeners.has(event)) { this.eventListeners.set(event, []); } this.eventListeners.get(event)!.push(callback); } private emit(event: string, data?: any): void { const listeners = this.eventListeners.get(event) || []; listeners.forEach(callback => callback(data)); } /** * Get queue metrics for business dashboard */ async getMetrics(): Promise<QueueMetrics | null> { if (!this.db) await this.initialize(); const transaction = this.db!.transaction(['queue_metrics'], 'readonly'); const store = transaction.objectStore('queue_metrics'); return new Promise((resolve) => { const request = store.get(new Date().toDateString()); request.onsuccess = () => resolve(request.result || null); request.onerror = () => resolve(null); }); } /** * Clear old completed operations (cleanup) */ async cleanup(olderThanDays: number = 7): Promise<void> { if (!this.db) return; const cutoffTime = Date.now() - (olderThanDays * 24 * 60 * 60 * 1000); const transaction = this.db.transaction(['chicken_operations'], 'readwrite'); const store = transaction.objectStore('chicken_operations'); const request = store.getAll(); request.onsuccess = () => { const operations = request.result as ChickenBusinessOperation[]; const toDelete = operations.filter(op => op.status === 'completed' && op.timestamp < cutoffTime ); toDelete.forEach(op => store.delete(op.id)); console.log(`🗑️ Cleaned up ${toDelete.length} old operations`); }; } } // Default instance for chicken business export const chickenBusinessQueue = new ChickenBusinessOfflineQueue(); // Auto-initialize chickenBusinessQueue.initialize().catch(console.error); export default ChickenBusinessOfflineQueue;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PSYGER02/mcpserver'

If you have feedback or need assistance with the MCP directory API, please join our Discord server