【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':
        // 自定义错误类型,继承自基本的McpError
        class 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 = 
### 关于MCPNode.js的关系以及如何在Node.js中使用MCP 目前,在提供的引用材料中并未提及具体的 **MCP (Media Coding Platform)** 或其 **Node.js** 的关系[^1]。然而,基于行业标准和技术背景可以推测两者可能的交互方式。 #### 什么是MCP? 假设这里的 **MCP** 是指一种多媒体编码平台或者框架,则它通常用于处理视频或音频数据的压缩、解码以及其他媒体流操作。例如,根据引用中的描述,“MPEG甜点区域大约为每像素1.2比特(帧内)和0.35比特(帧间),这表明优化后的DCT-量化-熵混合算法能够实现约6:1的压缩比率。” 这一特性意味着MCP可能是围绕类似的多媒体技术构建的一个工具集或库。 #### Node.js的作用 **Node.js** 是一个基于JavaScript运行时环境,主要用于开发服务器端应用程序和服务。由于它的异步I/O模型及其强大的生态系统支持各种插件模块,因此非常适合用来创建高性能网络应用,包括那些涉及实时音视频传输的应用程序。 如果要将MCP集成到Node.js项目里,以下是几种常见的方法: 1. **通过FFmpeg绑定**: 如果MCP依赖某些底层编解码器功能,那么可以通过流行的开源软件 `FFmpeg` 来间接调用这些能力。存在多个npm包可以帮助简化这一过程,比如 `fluent-ffmpeg` 和 `child_process` 模块可以直接执行命令行脚本并之通信。 下面是一个简单的例子展示如何利用子进程来启动外部可执行文件: ```javascript const { exec } = require('child_process'); exec('mcp_command --input input.mp4 --output output.mpg', (error, stdout, stderr) => { if (error) { console.error(`执行出错: ${stderr}`); return; } console.log(`成功完成转换: ${stdout}`); }); ``` 2. **原生扩展**: 对性能有极高需求的情况下,可以选择编写C++ Addon作为桥梁连接低级APIs至JS层面上去访问特定硬件加速指令集或是专有的SDK接口。不过这种方式复杂度较高且维护成本较大。 3. **RESTful API服务化部署**: 另外还有一种方案就是把整个MCP封装成独立的服务单元对外暴露HTTP REST endpoints供前端或其他微服务消费。这样做的好处是可以灵活切换不同的编程语言而无需修改太多客户端逻辑代码。 综上所述,虽然没有直接提到关于两者的具体关联定义,但从实际应用场景出发还是能找到不少可行的技术路线图来进行融合尝试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员查理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值