5.2 错误代码与解决方案
本章提供MCP协议中可能遇到的常见错误代码及其解决方案。在使用MCP开发应用时,了解这些错误及其处理方法可以帮助您更高效地调试和解决问题。
目录
错误分类
MCP错误可以大致分为以下几类:
- 连接错误: 与服务器建立连接时出现的问题
- 通信错误: 客户端和服务器之间通信过程中的问题
- 资源错误: 访问或处理资源时的问题
- 工具错误: 调用或执行工具时的问题
- 格式错误: 数据格式不正确或不兼容
- 运行时错误: 服务器或客户端运行过程中的异常
常见错误列表
连接错误
| 错误代码 | 错误名称 | 描述 | 解决方案 |
|---|---|---|---|
| 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("无法连接到服务器")
解决方案:
- 确认服务器地址和端口正确
- 检查服务器是否已启动
- 检查防火墙设置是否阻止了连接
- 尝试使用
telnet或curl测试连接
# 测试连接
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)
故障排除步骤
服务器端故障排除
-
启用详细日志
通过设置详细日志输出,可以获取更多的调试信息:
mcp = FastMCP("My Server", verbose=True) -
检查服务器日志
服务器日志通常包含有用的错误信息和堆栈跟踪:
import logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', filename='mcp_server.log' ) -
使用MCP检查器
MCP检查器可以帮助查看和测试服务器的资源、工具和提示:
mcp dev path/to/server.py -
加入异常处理
确保服务器代码中有适当的异常处理:
@mcp.tool() def sensitive_operation(data): try: # 操作... return result except Exception as e: logging.error(f"操作失败: {e}") # 返回适当的错误信息 raise ToolExecutionError(str(e)) -
验证资源和工具注册
确保所有资源和工具都已正确注册:
# 服务器启动时打印已注册的资源和工具 @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}")
客户端端故障排除
-
使用调试模式
启用客户端的调试模式:
import logging logging.basicConfig(level=logging.DEBUG) -
捕获和处理特定异常
根据预期的异常类型进行处理:
from mcp.errors import ResourceNotFoundError, ToolExecutionError try: # MCP操作... except ResourceNotFoundError: # 处理资源不存在的情况 except ToolExecutionError as e: # 处理工具执行错误 except Exception as e: # 处理其他异常 -
检查网络连接
验证网络连接是否正常:
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 -
验证请求和响应
打印请求和响应数据以进行调试:
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 -
使用重试机制
对于可能暂时失败的操作,实现重试机制:
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应用程序中的问题。
记住,良好的错误处理实践包括:
- 为用户提供明确的错误消息
- 实现适当的重试机制
- 记录详细的错误信息以便调试
- 为预期的异常设计恢复策略
下一章将介绍MCP的配置选项,帮助您优化和自定义MCP服务器和客户端的行为。