【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.pswp.cn/bicheng/78416.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78416.shtml
英文地址,請注明出處:http://en.pswp.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 需要…