当前位置: 首页 > news >正文

【MCP Node.js SDK 全栈进阶指南】中级篇(4):MCP错误处理与日志系统

前言

随着MCP应用的规模和复杂性增长,错误处理与日志系统的重要性也日益凸显。一个健壮的错误处理策略和高效的日志系统不仅可以帮助开发者快速定位和解决问题,还能提高应用的可靠性和可维护性。本文作为中级篇的第四篇,将深入探讨MCP TypeScript-SDK中的错误处理与日志系统,包括健壮错误处理策略、结构化日志设计、分布式环境下的日志管理以及监控与警报集成。

在MCP应用开发中,错误处理与日志记录是两个密不可分的主题。优秀的错误处理可以确保应用在遇到意外情况时能够优雅地降级或恢复,而全面的日志记录则为问题排查和性能分析提供了必要的信息基础。通过本文的学习,你将能够构建一个完善的错误处理与日志系统,使你的MCP应用更加可靠和易于维护。

一、健壮错误处理策略

MCP TypeScript-SDK提供了多层次的错误处理机制,帮助开发者构建健壮的应用。下面我们将从错误类型、错误捕获与处理、错误传播以及错误恢复与重试四个方面,详细探讨如何在MCP应用中实现健壮的错误处理策略。

1.1 MCP错误类型层次结构

了解MCP SDK中的错误类型层次结构,是实现有效错误处理的基础。MCP提供了一套丰富的错误类型,用于表示不同类别的错误:

import { McpServer } from '@modelcontextprotocol/sdk';
import { McpError,ValidationError,AuthenticationError,AuthorizationError,ResourceNotFoundError,ParameterValidationError,ToolExecutionError,TransportError,ServerInitializationError,TimeoutError,
} from '@modelcontextprotocol/sdk/errors';// 基本错误类型示例
const server = new McpServer({name: 'error-handling-server',description: 'MCP错误处理示例服务器',version: '1.0.0',
});// 注册一个资源,演示不同类型的错误
server.registerResource({name: 'error-examples',description: '展示不同类型的错误处理',params: {errorType: {type: 'string',enum: ['validation','authentication','authorization','notFound','parameter','tool','transport','timeout','custom',],description: '要模拟的错误类型',},},resolve: async (params, context) => {const { errorType } = params;// 根据参数抛出不同类型的错误switch (errorType) {case 'validation':throw new ValidationError('输入数据验证失败', {field: 'username',reason: '用户名必须至少包含3个字符',});case 'authentication':throw new AuthenticationError('身份验证失败', {reason: '无效的访问令牌',});case 'authorization':throw new AuthorizationError('没有足够的权限', {requiredPermission: 'admin:read',userPermissions: ['user:read'],});case 'notFound':throw new ResourceNotFoundError('请求的资源不存在', {resourceId: '12345',resourceType: 'user',});case 'parameter':throw new ParameterValidationError('参数验证失败', {parameter: 'age',reason: '年龄必须是一个正整数',value: -5,});case 'tool':throw new ToolExecutionError('工具执行失败', {toolName: 'dataProcessor',reason: '外部API调用超时',});case 'transport':throw new TransportError('传输层错误', {code: 'CONNECTION_RESET',target: 'http://api.example.com',});case 'timeout':throw new TimeoutError('操作超时', {operation: 'databaseQuery',timeoutMs: 5000,});case 'custom':// 自定义错误类型,继承自基本的McpErrorclass DataCorruptionError extends McpError {constructor(message: string, details?: any) {super('DATA_CORRUPTION', message, details);}}throw new DataCorruptionError('数据损坏错误', {dataSource: 'userDatabase',table: 'profiles',corrupted: ['name', 'email'],});default:return {content: '没有错误发生,一切正常',};}},
});

1.2 错误捕获与处理策略

在MCP应用中,错误可能发生在各个层面。以下是一种多层次的错误捕获与处理策略:

import { McpServer } from '@modelcontextprotocol/sdk';
import { McpError,createErrorHandler,createErrorMiddleware,
} from '@modelcontextprotocol/sdk/errors';// 创建MCP服务器
const server = new McpServer({name: 'error-handling-demo',description: '错误处理策略示例',version: '1.0.0',
});// 1. 全局错误处理器
const globalErrorHandler = createErrorHandler({// 默认错误处理程序,处理所有其他类型的错误default: (error, context) => {// 记录错误console.error(`全局错误: ${error.message}`, {errorType: error.type,details: error.details,stack: error.stack,context: {resourceName: context.resourceName,userId: context.auth?.userId,},});// 返回标准化错误响应return {error: {type: error.type || 'UNKNOWN_ERROR',message: error.message,code: error instanceof McpError ? error.code : 'INTERNAL_ERROR',},};},// 特定错误类型的处理程序handlers: {// 验证错误处理VALIDATION_ERROR: (error, context) => {return {error: {type: 'VALIDATION_ERROR',message: error.message,validationErrors: error.details,},};},// 身份验证错误处理AUTHENTICATION_ERROR: (error, context) => {// 可能需要引导用户重新登录return {error: {type: 'AUTHENTICATION_ERROR',message: '请重新登录以继续操作',redirectUrl: '/login',},};},// 授权错误处理AUTHORIZATION_ERROR: (error, context) => {return {error: {type: 'AUTHORIZATION_ERROR',message: '您没有执行此操作的权限',requiredPermissions: error.details?.requiredPermission,},};},// 资源未找到错误处理RESOURCE_NOT_FOUND: (error, context) => {return {error: {type: 'RESOURCE_NOT_FOUND',message: `找不到请求的资源: ${error.details?.resourceType || '未知'} (ID: ${error.details?.resourceId || '未知'})`,},};},// 超时错误处理TIMEOUT_ERROR: (error, context) => {return {error: {type: 'TIMEOUT_ERROR',message: '操作超时,请稍后重试',operation: error.details?.operation,suggestedRetryAfterMs: 5000,},};},},
});// 2. 中间件级别错误处理
const errorMiddleware = createErrorMiddleware({onError: async (error, req, context, next) => {// 记录请求详情和错误console.warn(`请求处理错误: ${req.resourceName}`, {params: req.params,error: {type: error.type,message: error.message,details: error.details,},});// 将某些错误类型转换为其他错误if (error.type === 'DATABASE_ERROR') {// 转换为更具体的错误类型if (error.details?.code === 'ER_NO_SUCH_TABLE') {return next(new McpError('SYSTEM_CONFIGURATION_ERROR', '系统配置错误,请联系管理员'));}}// 继续传递错误到下一个处理程序return next(error);},
});// 3. 资源级别错误处理
server.registerResource({name: 'user-profile',description: '用户个人资料',params: {userId: {type: 'string',description: '用户ID',},},// 资源级别错误处理errorHandler: {// 覆盖特定错误类型的处理RESOURCE_NOT_FOUND: (error, context) => {if (error.details?.resourceType === 'user') {// 提供更具体的、针对此资源的错误信息return {error: {type: 'USER_NOT_FOUND',message: `未找到ID为 ${error.details.resourceId} 的用户`,suggestions: ['检查用户ID是否正确','用户可能已被删除或禁用',],},};}// 否则使用默认处理return null;},},resolve: async (params, context) => {try {const { userId } = params;const user = await fetchUserProfile(userId);if (!user) {throw new ResourceNotFoundError('用户不存在', {resourceType: 'user',resourceId: userId,});}return {content: user,};} catch (error) {// 4. 本地错误处理(try-catch)if (error.name === 'DatabaseConnectionError') {// 转换数据库连接错误为MCP错误throw new McpError('SERVICE_UNAVAILABLE', '服务暂时不可用,请稍后重试', {originalError: {name: error.name,message: error.message,},});}// 其他错误重新抛出,由上层处理throw error;}},
});// 配置全局错误处理器
server.useErrorHandler(globalErrorHandler);// 配置错误处理中间件
server.useMiddleware(errorMiddleware);// 模拟获取用户资料的函数
async function fetchUserProfile(userId) {// 在实际应用中,这里会查询数据库const users = {'user-1': { id: 'user-1', name: '张三', email: 'zhang@example.com' },'user-2': { id: 'user-2', name: '李四', email: 'li@example.com' },};return users[userId] || null;
}

1.3 错误传播与聚合

在复杂的MCP应用中,错误可能需要在多个组件间传播,或者需要聚合多个错误。下面是一个错误传播与聚合的实现示例:

import { McpServer } from '@modelcontextprotocol/sdk';
import { McpError, AggregateError,ErrorChain,
} from '@modelcontextprotocol/sdk/errors';// 创建MCP服务器
const server = new McpServer({name: 'error-propagation-demo',description: '错误传播与聚合示例',version: '1.0.0',
});// 1. 错误链(错误传播)
server.registerResource({name: 'data-processor',description: '数据处理器示例',params: {dataId: {type: 'string',description: '要处理的数据ID',},},resolve: async (params, context) => {try {const { dataId } = params;// 调用一系列处理步骤const data = await fetchData(dataId);const processedData = await processData(data);const result = await saveProcessedData(processedData);return {content: result,};} catch (error) {// 创建错误链,保留错误发生的上下文throw new ErrorChain('数据处理失败', error, {operation: 'data-processor',dataId: params.dataId,timestamp: new Date().toISOString(),});}},
});// 2. 错误聚合(多个并行操作)
server.registerResource({name: 'batch-processor',description: '批处理多个操作',params: {items: {type: 'array',items: {type: 'string',},description: '要处理的项目ID列表',},},resolve: async (params, context) => {const { items } = params;// 并行处理多个项目const processingPromises = items.map(itemId => processItem(itemId));// 等待所有处理完成,即使有些会失败const results = await Promise.allSettled(processingPromises);// 收集成功的结果const successResults = results.filter(result => result.status === 'fulfilled').map(result => (result as PromiseFulfilledResult<any>).value);// 收集错误const errors = results.filter(result => result.status === 'rejected').map((result, index) => {const error = (result as PromiseRejectedResult).reason;return new McpError('ITEM_PROCESSING_ERROR',`处理项目 ${items[index]} 失败: ${error.message}`,{ itemId: items[index], originalError: error });});// 如果有错误,创建聚合错误if (errors.length > 0) {const errorRate = errors.length / items.length;// 如果错误率超过50%,则视为整体失败if (errorRate > 0.5) {throw new AggregateError('批处理大部分项目失败',errors,{failedCount: errors.length,totalCount: items.length,errorRate: errorRate,});}// 否则返回部分成功的结果和错误信息return {content: successResults,metadata: {successCount: successResults.length,failedCount: errors.length,totalCount: items.length,errors: errors.map(e => ({message: e.message,itemId: e.details.itemId,})),},};}// 所有项目处理成功return {content: successResults,metadata: {successCount: successResults.length,totalCount: items.length,},};},
});// 模拟数据获取和处理函数
async function fetchData(dataId) {// 模拟可能失败的数据获取操作if (dataId === 'invalid') {throw new McpError('DATA_FETCH_ERROR', '数据获取失败');}return { id: dataId, raw: '原始数据...' };
}async function processData(data) {// 模拟数据处理if (!data.raw) {throw new McpError('DATA_PROCESSING_ERROR', '无法处理空数据');}return { ...data, processed: '处理后的数据...' };
}async function saveProcessedData(data) {// 模拟数据保存if (data.id === 'unsavable') {throw new McpError('DATA_SAVE_ERROR', '无法保存处理后的数据');}return { ...data, savedAt: new Date().toISOString() };
}// 模拟单个项目处理
async function processItem(itemId) {// 模拟不同的处理结果if (itemId.includes('error')) {throw new Error(`处理 ${itemId} 时发生错误`);}// 模拟成功处理return {id: itemId,status: 'processed',timestamp: new Date().toISOString(),};
}

1.4 错误恢复与重试策略

处理临时错误(如网络中断、服务暂时不可用等)时,重试机制是一种有效的错误恢复策略。MCP SDK提供了强大的重试机制:

import { McpServer } from '@modelcontextprotocol/sdk';
import { createRetryPolicy,isRetryableError,withRetry,
} from '@modelcontextprotocol/sdk/errors/retry';// 创建MCP服务器
const server = new McpServer({name: 'retry-demo',description: '错误恢复与重试策略示例',version: '1.0.0',
});// 1. 创建重试策略
const retryPolicy = createRetryPolicy({// 最大重试次数maxRetries: 3,// 初始重试延迟(毫秒)initialDelay: 1000,// 延迟增长因子(指数退避)backoffFactor: 2,// 延迟抖动因子,增加随机性以避免"惊群效应"jitterFactor: 0.2,// 最大延迟时间(毫秒)maxDelay: 30000,// 决定错误是否可重试的函数isRetryable: (error) => {// 内置函数检查常见的可重试错误if (isRetryableError(error)) {return true;}// 自定义逻辑,例如特定HTTP状态码if (error.details?.statusCode) {const retryableStatusCodes = [429, 503, 504];return retryableStatusCodes.includes(error.details.statusCode);}// 根据错误类型判断return ['CONNECTION_ERROR','TIMEOUT_ERROR','RATE_LIMIT_ERROR','TEMPORARY_SERVER_ERROR',].includes(error.type);},// 重试前的钩子函数onRetry: (error, attempt, delay, context) => {console.warn(`重试操作 (尝试 ${attempt}/${retryPolicy.maxRetries})...`, {error: error.message,operation: context.operation,nextRetryDelay: delay,});},
});// 2. 注册使用重试策略的资源
server.registerResource({name: 'resilient-operation',description: '具有错误恢复能力的操作',params: {operation: {type: 'string',enum: ['network-call', 'database-query', 'external-api'],description: '要执行的操作类型',},shouldFail: {type: 'boolean',description: '是否模拟失败(用于测试)',default: false,},failCount: {type: 'number',description: '模拟连续失败的次数',default: 1,},},resolve: async (params, context) => {const { operation, shouldFail, failCount } = params;// 使用封装函数添加重试能力const result = 

相关文章:

  • Python SQL 工具包:SQLAlchemy介绍
  • UML 状态图:以共享汽车系统状态图为例
  • osxcross 搭建 macOS 交叉编译环境
  • 【数据结构】励志大厂版·初级(二刷复习)双链表
  • Mongodb分布式文件存储数据库
  • NineData 与飞书深度集成,企业级数据管理审批流程全面自动化
  • IDEA热加载
  • 逐位逼近法计算对数的小数部分
  • SpringClound 微服务分布式Nacos学习笔记
  • Docker--Docker网络原理
  • day35图像处理OpenCV
  • Java面向对象的三大特性
  • ClickHouse 设计与细节
  • Python 设计模式:模板模式
  • 安宝特方案 | 医疗AR眼镜,重新定义远程会诊体验
  • Qt -对象树
  • CSS预处理器对比:Sass、Less与Stylus如何选择
  • 操作系统之shell实现(下)
  • Laravel 对接阿里云 OSS 说明文档
  • GPIO(通用输入输出端口)详细介绍
  • “中华优秀科普图书榜”2024年度榜单揭晓
  • 2024年我国数字阅读用户规模达6.7亿
  • 具身智能资本盛宴:3个月37笔融资,北上深争锋BAT下场,人形机器人最火
  • 对话地铁读书人|翻译Esther:先读原著,再看电影
  • 瑞士成第15届北影节主宾国,6部佳作闪耀“瑞士电影周”
  • 韩国一战机飞行训练中掉落机炮吊舱和空油箱