UNPKG

yijing-bazi-mcp

Version:

易经八字分析 MCP 服务器 - 提供传统命理分析和易经卦象解读的AI工具

463 lines (404 loc) 12.7 kB
/** * 错误处理工具类 * 提供统一的错误处理、分类和响应机制 */ const Logger = require('./logger.js'); // const config = require('../config/config.js'); // 暂时注释掉,因为config.js不存在 /** * 错误类型枚举 */ const ErrorTypes = { VALIDATION_ERROR: 'VALIDATION_ERROR', CALCULATION_ERROR: 'CALCULATION_ERROR', DATA_ERROR: 'DATA_ERROR', SYSTEM_ERROR: 'SYSTEM_ERROR', NETWORK_ERROR: 'NETWORK_ERROR', TIMEOUT_ERROR: 'TIMEOUT_ERROR', PERMISSION_ERROR: 'PERMISSION_ERROR', RATE_LIMIT_ERROR: 'RATE_LIMIT_ERROR', MCP_ERROR: 'MCP_ERROR', UNKNOWN_ERROR: 'UNKNOWN_ERROR' }; /** * 错误严重级别 */ const ErrorSeverity = { LOW: 'low', MEDIUM: 'medium', HIGH: 'high', CRITICAL: 'critical' }; /** * 自定义错误类 */ class MCPError extends Error { constructor(message, type = ErrorTypes.UNKNOWN_ERROR, severity = ErrorSeverity.MEDIUM, details = {}) { super(message); this.name = 'MCPError'; this.type = type; this.severity = severity; this.details = details; this.timestamp = new Date().toISOString(); this.stack = Error.captureStackTrace ? Error.captureStackTrace(this, MCPError) : (new Error()).stack; } /** * 转换为JSON格式 * @returns {Object} JSON对象 */ toJSON() { return { name: this.name, message: this.message, type: this.type, severity: this.severity, details: this.details, timestamp: this.timestamp, stack: process.env.NODE_ENV === 'development' ? this.stack : undefined }; } /** * 获取用户友好的错误消息 * @returns {string} 用户友好的错误消息 */ getUserMessage() { const userMessages = { [ErrorTypes.VALIDATION_ERROR]: '输入参数有误,请检查后重试', [ErrorTypes.CALCULATION_ERROR]: '计算过程中出现错误,请稍后重试', [ErrorTypes.DATA_ERROR]: '数据处理错误,请检查输入数据', [ErrorTypes.SYSTEM_ERROR]: '系统内部错误,请联系管理员', [ErrorTypes.NETWORK_ERROR]: '网络连接错误,请检查网络状态', [ErrorTypes.TIMEOUT_ERROR]: '请求超时,请稍后重试', [ErrorTypes.PERMISSION_ERROR]: '权限不足,无法执行此操作', [ErrorTypes.RATE_LIMIT_ERROR]: '请求过于频繁,请稍后重试', [ErrorTypes.MCP_ERROR]: 'MCP协议错误,请检查客户端配置', [ErrorTypes.UNKNOWN_ERROR]: '未知错误,请联系技术支持' }; return userMessages[this.type] || this.message; } } /** * 错误处理器类 */ class ErrorHandler { constructor() { this.logger = new Logger(); this.errorStats = new Map(); this.setupGlobalHandlers(); } /** * 设置全局错误处理器 */ setupGlobalHandlers() { // 处理未捕获的异常 process.on('uncaughtException', (error) => { this.handleCriticalError(error, 'uncaughtException'); }); // 处理未处理的Promise拒绝 process.on('unhandledRejection', (reason, promise) => { this.handleCriticalError(reason, 'unhandledRejection', { promise }); }); // 处理警告 process.on('warning', (warning) => { this.logger.warn('Node.js警告', { name: warning.name, message: warning.message, stack: warning.stack }); }); } /** * 处理关键错误 * @param {Error} error - 错误对象 * @param {string} source - 错误来源 * @param {Object} context - 上下文信息 */ handleCriticalError(error, source, context = {}) { const mcpError = this.wrapError(error, ErrorTypes.SYSTEM_ERROR, ErrorSeverity.CRITICAL); this.logger.error('关键系统错误', { source, error: mcpError.toJSON(), context, pid: process.pid, memory: process.memoryUsage(), uptime: process.uptime() }); // 在生产环境中,关键错误可能需要重启服务 if (process.env.NODE_ENV === 'production' && source === 'uncaughtException') { this.logger.error('由于未捕获异常,服务将在5秒后退出'); setTimeout(() => { process.exit(1); }, 5000); } } /** * 包装错误为MCPError * @param {Error|string} error - 错误对象或消息 * @param {string} type - 错误类型 * @param {string} severity - 错误严重级别 * @param {Object} details - 错误详情 * @returns {MCPError} MCP错误对象 */ wrapError(error, type = ErrorTypes.UNKNOWN_ERROR, severity = ErrorSeverity.MEDIUM, details = {}) { if (error instanceof MCPError) { return error; } const message = error instanceof Error ? error.message : String(error); const mcpError = new MCPError(message, type, severity, { ...details, originalError: error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : null }); return mcpError; } /** * 处理工具调用错误 * @param {Error} error - 错误对象 * @param {string} toolName - 工具名称 * @param {Object} params - 工具参数 * @returns {Object} 错误响应 */ handleToolError(error, toolName, params = {}) { const mcpError = this.classifyError(error); // 记录错误统计 this.recordErrorStats(mcpError.type, toolName); // 记录错误日志 this.logger.error('工具调用错误', { tool: toolName, params: this.sanitizeParams(params), error: mcpError.toJSON() }); // 返回符合MCP协议的错误响应 return { content: [ { type: 'text', text: `工具调用失败: ${mcpError.getUserMessage()}` } ], isError: true }; } /** * 分类错误类型 * @param {Error} error - 错误对象 * @returns {MCPError} 分类后的错误 */ classifyError(error) { if (error instanceof MCPError) { return error; } let type = ErrorTypes.UNKNOWN_ERROR; let severity = ErrorSeverity.MEDIUM; // 根据错误消息和类型进行分类 if (error.name === 'ValidationError' || error.message.includes('validation')) { type = ErrorTypes.VALIDATION_ERROR; severity = ErrorSeverity.LOW; } else if (error.name === 'TypeError' || error.name === 'ReferenceError') { type = ErrorTypes.CALCULATION_ERROR; severity = ErrorSeverity.MEDIUM; } else if (error.name === 'SyntaxError') { type = ErrorTypes.DATA_ERROR; severity = ErrorSeverity.MEDIUM; } else if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') { type = ErrorTypes.NETWORK_ERROR; severity = ErrorSeverity.HIGH; } else if (error.code === 'ETIMEDOUT' || error.message.includes('timeout')) { type = ErrorTypes.TIMEOUT_ERROR; severity = ErrorSeverity.MEDIUM; } else if (error.code === 'EACCES' || error.code === 'EPERM') { type = ErrorTypes.PERMISSION_ERROR; severity = ErrorSeverity.HIGH; } else if (error.message.includes('rate limit')) { type = ErrorTypes.RATE_LIMIT_ERROR; severity = ErrorSeverity.LOW; } else if (error.message.includes('MCP') || error.message.includes('protocol')) { type = ErrorTypes.MCP_ERROR; severity = ErrorSeverity.HIGH; } return this.wrapError(error, type, severity); } /** * 创建错误响应 * @param {MCPError} error - MCP错误对象 * @param {string} toolName - 工具名称 * @returns {Object} 错误响应 */ createErrorResponse(error, toolName) { const response = { isError: true, error: { type: error.type, message: error.getUserMessage(), severity: error.severity, timestamp: error.timestamp, tool: toolName } }; // 在开发环境中包含更多调试信息 if (process.env.NODE_ENV === 'development') { response.error.details = error.details; response.error.stack = error.stack; response.error.originalMessage = error.message; } return response; } /** * 记录错误统计 * @param {string} errorType - 错误类型 * @param {string} toolName - 工具名称 */ recordErrorStats(errorType, toolName) { const key = `${toolName}:${errorType}`; const current = this.errorStats.get(key) || { count: 0, lastOccurred: null }; this.errorStats.set(key, { count: current.count + 1, lastOccurred: new Date().toISOString() }); // 定期清理统计数据 if (this.errorStats.size > 1000) { this.cleanupErrorStats(); } } /** * 清理错误统计数据 */ cleanupErrorStats() { const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1000); // 24小时前 for (const [key, stats] of this.errorStats.entries()) { if (new Date(stats.lastOccurred) < cutoff) { this.errorStats.delete(key); } } } /** * 获取错误统计 * @returns {Object} 错误统计数据 */ getErrorStats() { const stats = {}; for (const [key, data] of this.errorStats.entries()) { const [tool, type] = key.split(':'); if (!stats[tool]) { stats[tool] = {}; } stats[tool][type] = data; } return stats; } /** * 清理敏感参数 * @param {Object} params - 参数对象 * @returns {Object} 清理后的参数 */ sanitizeParams(params) { const sensitiveKeys = ['password', 'token', 'key', 'secret', 'auth']; const sanitized = { ...params }; for (const key of Object.keys(sanitized)) { if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) { sanitized[key] = '[REDACTED]'; } } return sanitized; } /** * 验证错误恢复 * @param {Function} operation - 要执行的操作 * @param {Object} options - 恢复选项 * @returns {Promise<*>} 操作结果 */ async withRetry(operation, options = {}) { const { maxAttempts = 3, delay = 1000, backoff = true, retryCondition = (error) => error.severity !== ErrorSeverity.CRITICAL } = options; let lastError; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = this.classifyError(error); // 检查是否应该重试 if (attempt === maxAttempts || !retryCondition(lastError)) { throw lastError; } // 计算延迟时间 const currentDelay = backoff ? delay * Math.pow(2, attempt - 1) : delay; this.logger.warn('操作失败,准备重试', { attempt, maxAttempts, delay: currentDelay, error: lastError.toJSON() }); // 等待后重试 await new Promise(resolve => setTimeout(resolve, currentDelay)); } } throw lastError; } /** * 创建验证错误 * @param {string} message - 错误消息 * @param {Object} details - 错误详情 * @returns {MCPError} 验证错误 */ createValidationError(message, details = {}) { return new MCPError(message, ErrorTypes.VALIDATION_ERROR, ErrorSeverity.LOW, details); } /** * 创建计算错误 * @param {string} message - 错误消息 * @param {Object} details - 错误详情 * @returns {MCPError} 计算错误 */ createCalculationError(message, details = {}) { return new MCPError(message, ErrorTypes.CALCULATION_ERROR, ErrorSeverity.MEDIUM, details); } /** * 创建数据错误 * @param {string} message - 错误消息 * @param {Object} details - 错误详情 * @returns {MCPError} 数据错误 */ createDataError(message, details = {}) { return new MCPError(message, ErrorTypes.DATA_ERROR, ErrorSeverity.MEDIUM, details); } /** * 创建系统错误 * @param {string} message - 错误消息 * @param {Object} details - 错误详情 * @returns {MCPError} 系统错误 */ createSystemError(message, details = {}) { return new MCPError(message, ErrorTypes.SYSTEM_ERROR, ErrorSeverity.HIGH, details); } /** * 通用错误处理方法 * @param {Error} error - 错误对象 * @param {string} context - 错误上下文 * @param {Object} details - 额外详情 * @returns {MCPError} 处理后的错误 */ handleError(error, context = 'unknown', details = {}) { const mcpError = this.classifyError(error); // 记录错误日志 this.logger.error('错误处理', { context, error: mcpError.toJSON(), details }); return mcpError; } } // 导出错误类型和处理器 module.exports = { ErrorTypes, ErrorSeverity, MCPError, ErrorHandler }; module.exports.default = ErrorHandler;