273 lines
7.6 KiB
JavaScript
273 lines
7.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* @fileoverview Main entry point for Laravel Healthcare MCP Server
|
|
* Initializes and starts the MCP server with all components
|
|
*/
|
|
|
|
import { ConfigManager } from './src/config/ConfigManager.js';
|
|
import { AuthManager } from './src/auth/AuthManager.js';
|
|
import { ApiClient } from './src/proxy/ApiClient.js';
|
|
import { ToolGenerator } from './src/tools/ToolGenerator.js';
|
|
import { McpServer } from './src/server/McpServer.js';
|
|
import { logger, auditLog } from './src/utils/logger.js';
|
|
import { ErrorHandler } from './src/utils/errors.js';
|
|
|
|
/**
|
|
* Main application class
|
|
*/
|
|
class HealthcareMcpServerApp {
|
|
constructor() {
|
|
this.config = null;
|
|
this.authManager = null;
|
|
this.apiClient = null;
|
|
this.toolGenerator = null;
|
|
this.mcpServer = null;
|
|
this.isShuttingDown = false;
|
|
}
|
|
|
|
/**
|
|
* Initialize the application
|
|
*/
|
|
async initialize() {
|
|
try {
|
|
logger.info('Initializing Laravel Healthcare MCP Server...');
|
|
|
|
// Load configuration
|
|
this.config = new ConfigManager();
|
|
logger.info('Configuration loaded:', this.config.getSummary());
|
|
|
|
// Validate configuration
|
|
const configValidation = this.config.isValid();
|
|
if (!configValidation) {
|
|
throw new Error('Configuration validation failed');
|
|
}
|
|
|
|
// Initialize authentication manager
|
|
this.authManager = new AuthManager(null, this.config.getAll(true));
|
|
logger.info('Authentication manager initialized');
|
|
|
|
// Initialize API client
|
|
this.apiClient = new ApiClient(this.config.getAll(), this.authManager);
|
|
logger.info('API client initialized');
|
|
|
|
// Initialize tool generator
|
|
this.toolGenerator = new ToolGenerator(this.apiClient);
|
|
logger.info('Tool generator initialized');
|
|
|
|
// Initialize MCP server
|
|
this.mcpServer = new McpServer(this.config.getAll(), this.toolGenerator);
|
|
logger.info('MCP server initialized');
|
|
|
|
// Validate authentication credentials (optional)
|
|
if (this.config.get('NODE_ENV') !== 'production') {
|
|
await this.validateAuthCredentials();
|
|
}
|
|
|
|
logger.info('Application initialization completed successfully');
|
|
|
|
} catch (error) {
|
|
logger.error('Failed to initialize application:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate authentication credentials for all configured auth types
|
|
*/
|
|
async validateAuthCredentials() {
|
|
try {
|
|
logger.info('Validating authentication credentials...');
|
|
|
|
const results = await this.authManager.validateAllCredentials();
|
|
const validCredentials = [];
|
|
const invalidCredentials = [];
|
|
|
|
Object.entries(results).forEach(([authType, result]) => {
|
|
if (result.valid) {
|
|
validCredentials.push(authType);
|
|
} else {
|
|
invalidCredentials.push({ authType, error: result.error });
|
|
}
|
|
});
|
|
|
|
logger.info(`Authentication validation completed: ${validCredentials.length} valid, ${invalidCredentials.length} invalid`);
|
|
|
|
if (validCredentials.length > 0) {
|
|
logger.info('Valid credentials for:', validCredentials);
|
|
}
|
|
|
|
if (invalidCredentials.length > 0) {
|
|
logger.warn('Invalid credentials:', invalidCredentials.map(c => `${c.authType}: ${c.error}`));
|
|
}
|
|
|
|
} catch (error) {
|
|
logger.warn('Authentication validation failed:', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start the MCP server
|
|
*/
|
|
async start() {
|
|
try {
|
|
logger.info('Starting Laravel Healthcare MCP Server...');
|
|
|
|
// Setup graceful shutdown handlers
|
|
this.setupShutdownHandlers();
|
|
|
|
// Start the MCP server
|
|
await this.mcpServer.start();
|
|
|
|
// Log startup completion
|
|
const stats = this.mcpServer.getStatistics();
|
|
logger.info('Server started successfully:', {
|
|
toolCount: stats.toolCount,
|
|
categories: Object.keys(stats.categorySummary).length,
|
|
authTypes: Object.keys(stats.authTypeSummary).length
|
|
});
|
|
|
|
// Audit log
|
|
auditLog('server_started', 'system', {
|
|
serverName: this.config.get('MCP_SERVER_NAME'),
|
|
serverVersion: this.config.get('MCP_SERVER_VERSION'),
|
|
toolCount: stats.toolCount
|
|
});
|
|
|
|
logger.info('Laravel Healthcare MCP Server is ready to accept connections');
|
|
|
|
} catch (error) {
|
|
logger.error('Failed to start server:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop the MCP server
|
|
*/
|
|
async stop() {
|
|
if (this.isShuttingDown) {
|
|
return;
|
|
}
|
|
|
|
this.isShuttingDown = true;
|
|
logger.info('Shutting down Laravel Healthcare MCP Server...');
|
|
|
|
try {
|
|
// Stop MCP server
|
|
if (this.mcpServer) {
|
|
await this.mcpServer.stop();
|
|
logger.info('MCP server stopped');
|
|
}
|
|
|
|
// Clear authentication tokens
|
|
if (this.authManager) {
|
|
this.authManager.clearAllTokens();
|
|
logger.info('Authentication tokens cleared');
|
|
}
|
|
|
|
// Audit log
|
|
auditLog('server_stopped', 'system', {
|
|
reason: 'graceful_shutdown'
|
|
});
|
|
|
|
logger.info('Server shutdown completed');
|
|
|
|
} catch (error) {
|
|
logger.error('Error during shutdown:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup graceful shutdown handlers
|
|
*/
|
|
setupShutdownHandlers() {
|
|
const shutdownHandler = async (signal) => {
|
|
logger.info(`Received ${signal}, initiating graceful shutdown...`);
|
|
await this.stop();
|
|
process.exit(0);
|
|
};
|
|
|
|
// Handle various shutdown signals
|
|
process.on('SIGTERM', () => shutdownHandler('SIGTERM'));
|
|
process.on('SIGINT', () => shutdownHandler('SIGINT'));
|
|
process.on('SIGUSR2', () => shutdownHandler('SIGUSR2')); // nodemon restart
|
|
|
|
// Handle uncaught exceptions
|
|
process.on('uncaughtException', (error) => {
|
|
logger.error('Uncaught exception:', error);
|
|
auditLog('server_error', 'system', { error: error.message, type: 'uncaught_exception' });
|
|
this.stop().then(() => process.exit(1));
|
|
});
|
|
|
|
// Handle unhandled promise rejections
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
logger.error('Unhandled promise rejection:', { reason, promise });
|
|
auditLog('server_error', 'system', { error: reason, type: 'unhandled_rejection' });
|
|
this.stop().then(() => process.exit(1));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get application health status
|
|
*/
|
|
getHealthStatus() {
|
|
if (!this.mcpServer) {
|
|
return { healthy: false, reason: 'Server not initialized' };
|
|
}
|
|
|
|
try {
|
|
const serverHealth = this.mcpServer.getHealthStatus();
|
|
const apiHealth = this.apiClient.getHealthStatus();
|
|
const authHealth = this.authManager.getCacheStats();
|
|
|
|
return {
|
|
healthy: true,
|
|
timestamp: new Date().toISOString(),
|
|
server: serverHealth,
|
|
api: apiHealth,
|
|
auth: authHealth,
|
|
uptime: process.uptime(),
|
|
memory: process.memoryUsage(),
|
|
version: this.config.get('MCP_SERVER_VERSION')
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
healthy: false,
|
|
reason: error.message,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main execution function
|
|
*/
|
|
async function main() {
|
|
const app = new HealthcareMcpServerApp();
|
|
|
|
try {
|
|
// Initialize application
|
|
await app.initialize();
|
|
|
|
// Start server
|
|
await app.start();
|
|
|
|
} catch (error) {
|
|
logger.error('Application startup failed:', error);
|
|
ErrorHandler.logError(error, logger, { context: 'application_startup' });
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run the application if this file is executed directly
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
main().catch((error) => {
|
|
console.error('Fatal error:', error);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
export { HealthcareMcpServerApp };
|