【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 = 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt】文件

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Qt 目录 一&#xff1a;&#x1f525; Qt 文件概述 二&#xff1a;&#x1f525; 输入输出设备类 三&#xff1a;&#x1f525; 文件读写类 四&#xff1a;&#x1f525; 文件和目录信息类 五&…

代码随想录算法训练营第五十八天 | 1.拓扑排序精讲 2.dijkstra(朴素版)精讲 卡码网117.网站构建 卡码网47.参加科学大会

1.拓扑排序精讲 题目链接&#xff1a;117. 软件构建 文章讲解&#xff1a;代码随想录 思路&#xff1a; 把有向无环图进行线性排序的算法都可以叫做拓扑排序。 实现拓扑排序的算法有两种&#xff1a;卡恩算法&#xff08;BFS&#xff09;和DFS&#xff0c;以下BFS的实现思…

Qt实现语言切换的完整方案

在Qt中实现语言动态切换需要以下几个关键步骤&#xff0c;我将提供一个完整的实现方案&#xff1a; 一、准备工作 在代码中使用tr()标记所有需要翻译的字符串 cpp button->setText(tr("Submit")); 创建翻译文件 在.pro文件中添加&#xff1a; qmake TRANSLATION…

面试中被问到mybatis与jdbc有什么区别怎么办

1. 核心区别 维度JDBCMyBatis抽象层级底层API&#xff0c;直接操作数据库高层持久层框架&#xff0c;封装JDBC细节代码量需要手动编写大量样板代码&#xff08;连接、异常处理等&#xff09;通过配置和映射减少冗余代码SQL管理SQL嵌入Java代码&#xff0c;维护困难SQL与Java代…

用于协同显著目标检测的小组协作学习 2021 GCoNet(总结)

摘要 一 介绍 问题一&#xff1a;以往的研究尝试利用相关图像之间的一致性&#xff0c;通过探索不同的共享线索[12, 13, 14]或语义连接[15, 16, 17]&#xff0c;来助力图像组内的共同显著目标检测&#xff08;CoSOD&#xff09;&#xff0c;什么意思&#xff1f; 一方面是探…

OpenCV 图形API(62)特征检测-----在图像中查找最显著的角点函数goodFeaturesToTrack()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 确定图像上的强角点。 该函数在图像或指定的图像区域内找到最显著的角点&#xff0c;如文献[240]中所述。 函数使用 cornerMinEigenVal 或 cor…

MySQL引擎分类与选择、SQL更新底层实现、分库分表、读写分离、主从复制 - 面试实战

MySQL引擎分类与选择、SQL更新底层实现、分库分表、读写分离、主从复制 - 面试实战 故事背景&#xff1a; 今天&#xff0c;我们模拟一场互联网大厂Java求职者的面试场景。面试官将针对MySQL的核心技术点进行提问&#xff0c;涵盖MySQL引擎分类与选择、SQL更新底层实现、分库…

如何确保微型导轨的质量稳定?

微型导轨在精密机械中扮演着至关重要的角色&#xff0c;它们不仅影响设备的性能&#xff0c;还决定了产品的寿命。那么&#xff0c;如何通过一些关键步骤来提高微型导轨的稳定性呢&#xff1f; 1、严格筛选供应商&#xff1a;选择具备高品质保证能力的供应商&#xff0c;确保原…

Golang编程拒绝类型不安全

简介 在 Go 中&#xff0c;标准库提供了多种容器类型&#xff0c;如 list、ring、heap、sync.Pool 和 sync.Map。然而&#xff0c;这些容器默认是类型不安全的&#xff0c;即它们可以接受任何类型的值&#xff0c;这可能导致运行时错误。为了提升代码的类型安全性和可维护性&am…

什么是 JSON?学习JSON有什么用?在springboot项目里如何实现JSON的序列化和反序列化?

作为一个学习Javaweb的新手&#xff0c;理解JSON的序列化和反序列化非常重要&#xff0c;因为它在现代Web开发&#xff0c;特别是Spring Boot中无处不在。 什么是 JSON&#xff1f; 首先&#xff0c;我们简单了解一下JSON (JavaScript Object Notation)。 JSON 是一种轻量级的…

iOS/Android 使用 C++ 跨平台模块时的内存与生命周期管理

在移动应用开发领域,跨平台开发已经成为一种不可忽视的趋势。随着智能手机市场的持续扩张,开发者需要同时满足iOS和Android两大主流平台的需求,而这往往意味着重复的工作量和高昂的维护成本。跨平台开发的目标在于通过一套代码库实现多平台的支持,从而降低开发成本、加速产…

【AAudio】A2dp sink创建音频轨道的源码流程分析

一、AAudio概述 AAudio 是 Android 8.0(API 级别 26)引入的 C/C++ 原生音频 API,专为需要低延迟、高性能音频处理的应用设计,尤其适用于实时音频应用(如音频合成器、音乐制作工具、游戏音效等)。 1.1 主要特点 低延迟:通过减少音频数据在内核与用户空间之间的拷贝,直…

Spring中配置 Bean 的两种方式:XML 配置 和 Java 配置类

在 Spring 框架中,配置 Bean 的方式主要有两种:XML 配置 和 Java 配置类。这两种方式都可以实现将对象注册到 Spring 容器中,并通过依赖注入进行管理。本文将详细介绍这两种配置方式的步骤,并提供相应的代码示例。 1. 使用 XML 配置的方式 步骤 创建 Spring 配置文件 创建…

海之淀攻略

家长要做的功课 家长可根据孩子情况&#xff0c;需要做好以下功课&#xff1a; 未读小学的家长&#xff1a;了解小学小升初派位初中校额到校在读小学的家长&#xff1a;了解小升初派位初中校额到校在读初中的家长&#xff1a;了解初中校额到校 越是高年级的家长&#xff0c;…

BUUCTF-[GWCTF 2019]re3

[GWCTF 2019]re3 查壳&#xff0c;64位无壳 然后进去发现主函数也比较简单&#xff0c;主要是一个长度校验&#xff0c;然后有一个mprotect函数&#xff0c;说明应该又是Smc&#xff0c;然后我们用脚本还原sub_402219函数处的代码 import idc addr0x00402219 size224 for …

sql server 开启cdc报事务正在执行

今天开启数据库cdc 功能的时候提示&#xff1a;一个dbrole 的存储过程&#xff0c;rolemember cdc db_ower, &#xff0c;有事务正在进行&#xff0c;执行失败。 执行多次仍然如此&#xff0c;开启cdc的存储过程是sys.sp_cdc_enable_db;查询了一下网络&#xff0c;给出的方…

2025年GPLT团体程序设计天梯赛L1-L2

目录 1.珍惜生命 2.偷感好重 3.高温补贴 4.零头就抹了吧 5.这是字符串题 6.这不是字符串题 7.大幂数​编辑 8.现代战争​编辑 9.算式拆解 10.三点共线 11.胖达的山头 12.被n整除的n位数 1.珍惜生命 【解析】直接输出即可 #include<bits/stdc.h> using namespace…

promethus基础

1.下载prometheus并解压 主要配置prometheus.yml文件 在scrape_configs配置项下添加配置(hadoop202是主机名)&#xff1a; scrape_configs: job_name: ‘prometheus’ static_configs: targets: [‘hadoop202:9090’] 添加 PushGateway 监控配置 job_name: ‘pushgateway’…

缓存与数据库数据一致性:旁路缓存、读写穿透和异步写入模式解析

旁路缓存模式、读写穿透模式和异步缓存写入模式是三种常见的缓存使用模式&#xff0c;以下是对三种经典缓存使用模式在缓存与数据库数据一致性方面更全面的分析&#xff1a; 一、旁路缓存模式&#xff08;Cache - Aside Pattern&#xff09; 1.数据读取流程 应用程序首先向缓…

【ESP32S3】 下载时遇到 libusb_open() failed 解决方案

之前写过一篇 《VSCode 开发环境搭建》 的文章&#xff0c;很多小伙伴反馈说在下载固件或者配置的时候会报错&#xff0c;提示大多是 libusb_open() failed ...... &#xff1a; 这其实是由于 USB 驱动不正确导致的&#xff0c;准确来说应该是与 ESP-IDF 中内置的 OpenOCD 需要…