5.2_错误代码与解决方案

5.2 错误代码与解决方案

本章提供MCP协议中可能遇到的常见错误代码及其解决方案。在使用MCP开发应用时,了解这些错误及其处理方法可以帮助您更高效地调试和解决问题。

目录

错误分类

MCP错误可以大致分为以下几类:

  1. 连接错误: 与服务器建立连接时出现的问题
  2. 通信错误: 客户端和服务器之间通信过程中的问题
  3. 资源错误: 访问或处理资源时的问题
  4. 工具错误: 调用或执行工具时的问题
  5. 格式错误: 数据格式不正确或不兼容
  6. 运行时错误: 服务器或客户端运行过程中的异常

常见错误列表

连接错误

错误代码 错误名称 描述 解决方案
1001 ConnectionRefused 无法连接到MCP服务器 检查服务器地址是否正确,服务器是否已启动,以及网络连接是否正常
1002 ConnectionTimeout 连接超时 增加超时时间,检查网络延迟,或检查服务器负载
1003 HandshakeError 握手失败 检查协议版本兼容性,以及客户端和服务器的配置
1004 AuthenticationError 身份验证失败 检查身份验证凭据是否正确,以及权限设置
1005 TransportError 传输层错误 根据具体的传输方式(stdio、SSE、WebSocket等)进行相应的故障排除

示例与解决方案

ConnectionRefused错误:

# 错误示例
try:
    async with sse_client("http://localhost:8000/mcp") as (read, write):
        # ...
except ConnectionRefusedError:
    print("无法连接到服务器")

解决方案:

  1. 确认服务器地址和端口正确
  2. 检查服务器是否已启动
  3. 检查防火墙设置是否阻止了连接
  4. 尝试使用 telnetcurl 测试连接
# 测试连接
telnet localhost 8000
# 或
curl http://localhost:8000/mcp

通信错误

错误代码 错误名称 描述 解决方案
2001 MessageFormatError 消息格式错误 检查消息格式是否符合MCP规范
2002 MessageTooLarge 消息过大 减小消息大小,或增加服务器的最大消息大小限制
2003 RequestTimeout 请求超时 增加超时设置,或优化服务器响应时间
2004 SessionExpired 会话已过期 重新初始化会话
2005 ProtocolVersionMismatch 协议版本不匹配 更新客户端或服务器以使用兼容的协议版本

示例与解决方案

SessionExpired错误:

try:
    result = await session.call_tool("calculator", arguments={"expression": "2 + 2"})
except SessionExpiredError:
    # 重新初始化会话
    await session.initialize()
    result = await session.call_tool("calculator", arguments={"expression": "2 + 2"})

资源错误

错误代码 错误名称 描述 解决方案
3001 ResourceNotFound 资源不存在 检查资源URI是否正确,以及资源是否已注册
3002 ResourceAccessDenied 资源访问被拒绝 检查访问权限设置
3003 InvalidResourceArguments 资源参数无效 检查提供的参数类型和值是否符合预期
3004 ResourceContentError 资源内容错误 检查资源内容的格式和有效性
3005 ResourceUriMalformed 资源URI格式错误 检查URI格式是否符合规范

示例与解决方案

ResourceNotFound错误:

try:
    data = await session.read_resource("data://users/123")
except ResourceNotFoundError:
    # 处理资源不存在的情况
    print("用户数据不存在")
    # 可能需要创建资源或使用默认值

工具错误

错误代码 错误名称 描述 解决方案
4001 ToolNotFound 工具不存在 检查工具名称是否正确,以及工具是否已注册
4002 ToolExecutionError 工具执行错误 检查工具实现逻辑,以及提供的参数
4003 InvalidToolArguments 工具参数无效 检查提供的参数类型和值是否符合预期
4004 ToolTimeout 工具执行超时 优化工具实现,或增加超时设置
4005 ToolPermissionDenied 工具执行权限被拒绝 检查权限设置

示例与解决方案

InvalidToolArguments错误:

try:
    result = await session.call_tool(
        "calculate_bmi", 
        arguments={"weight_kg": "not_a_number", "height_m": 1.75}
    )
except InvalidToolArgumentsError as e:
    print(f"参数错误: {e}")
    # 使用正确的参数类型重试
    result = await session.call_tool(
        "calculate_bmi", 
        arguments={"weight_kg": 70.0, "height_m": 1.75}
    )

格式错误

错误代码 错误名称 描述 解决方案
5001 InvalidJson JSON格式无效 检查JSON数据格式
5002 InvalidSchema 数据模式无效 验证对象是否符合预期的JSON Schema
5003 ContentTypeMismatch 内容类型不匹配 确保内容类型与期望的类型一致
5004 EncodingError 编码错误 检查字符编码和特殊字符处理
5005 DecodingError 解码错误 检查收到的数据格式与编码类型

示例与解决方案

InvalidJson错误:

import json

# 防止InvalidJson错误的辅助函数
def safe_json_parse(data):
    try:
        return json.loads(data)
    except json.JSONDecodeError:
        print("JSON格式无效")
        return None

运行时错误

错误代码 错误名称 描述 解决方案
6001 ServerError 服务器内部错误 检查服务器日志以获取详细信息
6002 ClientError 客户端内部错误 检查客户端代码和配置
6003 MemoryError 内存不足 优化内存使用,或增加可用内存
6004 ConcurrencyError 并发相关错误 检查并发处理逻辑
6005 LifecycleError 生命周期相关错误 确保按正确顺序调用生命周期方法

示例与解决方案

ServerError错误:

try:
    result = await session.call_tool("complex_calculation", arguments={"data": large_data})
except ServerError as e:
    print(f"服务器错误: {e}")
    # 尝试使用更小的数据集或分批处理
    chunked_results = []
    for chunk in split_data(large_data, chunk_size=1000):
        chunk_result = await session.call_tool("complex_calculation", arguments={"data": chunk})
        chunked_results.append(chunk_result)

故障排除步骤

服务器端故障排除

  1. 启用详细日志

    通过设置详细日志输出,可以获取更多的调试信息:

    mcp = FastMCP("My Server", verbose=True)
    
  2. 检查服务器日志

    服务器日志通常包含有用的错误信息和堆栈跟踪:

    import logging
    
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        filename='mcp_server.log'
    )
    
  3. 使用MCP检查器

    MCP检查器可以帮助查看和测试服务器的资源、工具和提示:

    mcp dev path/to/server.py
    
  4. 加入异常处理

    确保服务器代码中有适当的异常处理:

    @mcp.tool()
    def sensitive_operation(data):
        try:
            # 操作...
            return result
        except Exception as e:
            logging.error(f"操作失败: {e}")
            # 返回适当的错误信息
            raise ToolExecutionError(str(e))
    
  5. 验证资源和工具注册

    确保所有资源和工具都已正确注册:

    # 服务器启动时打印已注册的资源和工具
    @mcp.on_initialize
    async def initialize(ctx):
        resources = [r for r in mcp._resources]
        tools = [t for t in mcp._tools]
        print(f"已注册资源: {resources}")
        print(f"已注册工具: {tools}")
    

客户端端故障排除

  1. 使用调试模式

    启用客户端的调试模式:

    import logging
    logging.basicConfig(level=logging.DEBUG)
    
  2. 捕获和处理特定异常

    根据预期的异常类型进行处理:

    from mcp.errors import ResourceNotFoundError, ToolExecutionError
    
    try:
        # MCP操作...
    except ResourceNotFoundError:
        # 处理资源不存在的情况
    except ToolExecutionError as e:
        # 处理工具执行错误
    except Exception as e:
        # 处理其他异常
    
  3. 检查网络连接

    验证网络连接是否正常:

    import socket
    
    def check_server_connection(host, port):
        try:
            socket.create_connection((host, port), timeout=5)
            return True
        except (socket.timeout, socket.error):
            return False
    
  4. 验证请求和响应

    打印请求和响应数据以进行调试:

    async def debug_tool_call(session, tool_name, arguments):
        print(f"调用工具: {tool_name}")
        print(f"参数: {arguments}")
        try:
            result = await session.call_tool(tool_name, arguments)
            print(f"结果: {result}")
            return result
        except Exception as e:
            print(f"错误: {e}")
            raise
    
  5. 使用重试机制

    对于可能暂时失败的操作,实现重试机制:

    async def call_with_retry(session, tool_name, arguments, max_retries=3, delay=1):
        retries = 0
        while retries < max_retries:
            try:
                return await session.call_tool(tool_name, arguments)
            except (ConnectionError, RequestTimeout) as e:
                retries += 1
                if retries >= max_retries:
                    raise
                print(f"重试 ({retries}/{max_retries}): {e}")
                await asyncio.sleep(delay)
    

社区解决方案

以下是社区中常见问题的解决方案:

1. 处理大型资源

当需要处理大型资源时,可能会遇到性能问题或超时。社区解决方案:

# 服务器端:分页资源
@mcp.resource("data://large-collection")
def get_large_collection(page: int = 0, page_size: int = 100) -> dict:
    """获取大型集合的一部分"""
    start = page * page_size
    end = start + page_size
    return {
        "data": large_data[start:end],
        "pagination": {
            "page": page,
            "pageSize": page_size,
            "totalItems": len(large_data),
            "totalPages": (len(large_data) + page_size - 1) // page_size
        }
    }

# 客户端:处理分页
async def fetch_all_items(session):
    all_items = []
    page = 0
    while True:
        response = await session.read_resource(
            "data://large-collection",
            arguments={"page": page, "page_size": 100}
        )
        
        content = response.content[0].json
        all_items.extend(content["data"])
        
        if page >= content["pagination"]["totalPages"] - 1:
            break
            
        page += 1
    
    return all_items

2. 处理不稳定的连接

对于不稳定的网络连接,社区常用的解决方案:

class ResilientMcpClient:
    def __init__(self, server_url, max_retries=5, retry_delay=1):
        self.server_url = server_url
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        self.session = None
    
    async def ensure_connected(self):
        if self.session is None or not self.session.is_active():
            retries = 0
            while retries < self.max_retries:
                try:
                    transport = await sse_client(self.server_url)
                    read, write = transport
                    self.session = ClientSession(read, write)
                    await self.session.initialize()
                    return
                except Exception as e:
                    retries += 1
                    if retries >= self.max_retries:
                        raise ConnectionError(f"无法连接到MCP服务器: {e}")
                    await asyncio.sleep(self.retry_delay * (2 ** (retries - 1)))  # 指数退避
    
    async def call_tool(self, tool_name, arguments):
        await self.ensure_connected()
        try:
            return await self.session.call_tool(tool_name, arguments)
        except SessionExpiredError:
            self.session = None
            await self.ensure_connected()
            return await self.session.call_tool(tool_name, arguments)

3. 工具超时处理

复杂工具可能需要更长的执行时间,社区解决方案:

# 服务器端:异步工具执行
@mcp.tool()
async def long_running_operation(task_id: str) -> dict:
    """启动长时间运行的操作并返回任务ID"""
    # 将任务加入队列并立即返回
    task = start_background_task(task_id)
    return {"task_id": task_id, "status": "started"}

@mcp.tool()
async def get_task_status(task_id: str) -> dict:
    """获取任务状态"""
    status = check_task_status(task_id)
    return {"task_id": task_id, "status": status}

# 客户端:轮询任务状态
async def execute_long_running_task(session, task_id):
    # 启动任务
    await session.call_tool("long_running_operation", arguments={"task_id": task_id})
    
    # 轮询状态
    status = "started"
    while status not in ["completed", "failed"]:
        await asyncio.sleep(5)  # 等待5秒
        response = await session.call_tool("get_task_status", arguments={"task_id": task_id})
        status = response.content[0].json["status"]
    
    if status == "completed":
        # 获取结果
        result = await session.read_resource(f"data://task-results/{task_id}")
        return result
    else:
        raise Exception(f"任务失败: {task_id}")

小结

本章详细介绍了MCP协议中可能遇到的常见错误及其解决方案。通过理解这些错误代码和故障排除步骤,您将能够更有效地调试和解决MCP应用程序中的问题。

记住,良好的错误处理实践包括:

  1. 为用户提供明确的错误消息
  2. 实现适当的重试机制
  3. 记录详细的错误信息以便调试
  4. 为预期的异常设计恢复策略

下一章将介绍MCP的配置选项,帮助您优化和自定义MCP服务器和客户端的行为。

使用 Hugo 构建
主题 StackJimmy 设计