fix(sse): properly handle MCP tool errors and send error responses to client

- Enhance exception handling in agent_task() to capture and send structured error messages via SSE stream
- Add [DONE] marker to outer exception handler to ensure proper stream termination
- Improve MCP tool loading error handling in init_agent() to prevent cascading failures
- Add detailed error logging with traceback for debugging

Fixes RemoteProtocolError that occurred when MCP tool calls failed,
which previously caused incomplete chunked read errors and connection drops.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
朱潮 2026-01-07 19:24:28 +08:00
parent f74f09c191
commit 8a85e9025e
2 changed files with 27 additions and 3 deletions

View File

@ -102,8 +102,11 @@ async def get_tools_from_mcp(mcp):
logger.info(f"get_tools_from_mcp: loaded {len(mcp_tools)} tools, elapsed: {time.time() - start_time:.3f}s") logger.info(f"get_tools_from_mcp: loaded {len(mcp_tools)} tools, elapsed: {time.time() - start_time:.3f}s")
return mcp_tools return mcp_tools
except Exception as e: except Exception as e:
import traceback
error_details = traceback.format_exc()
# 发生异常时返回空列表,避免上层调用报错 # 发生异常时返回空列表,避免上层调用报错
logger.info(f"get_tools_from_mcp: error {e}, elapsed: {time.time() - start_time:.3f}s") logger.error(f"get_tools_from_mcp: error {str(e)}, elapsed: {time.time() - start_time:.3f}s")
logger.error(f"Full traceback: {error_details}")
return [] return []
async def init_agent(config: AgentConfig): async def init_agent(config: AgentConfig):
@ -139,7 +142,12 @@ async def init_agent(config: AgentConfig):
config.mcp_settings = system_prompt config.mcp_settings = system_prompt
# 获取 mcp_tools缓存逻辑已内置到 get_tools_from_mcp 中) # 获取 mcp_tools缓存逻辑已内置到 get_tools_from_mcp 中)
try:
mcp_tools = await get_tools_from_mcp(mcp_settings) mcp_tools = await get_tools_from_mcp(mcp_settings)
logger.info(f"Successfully loaded {len(mcp_tools)} MCP tools")
except Exception as e:
logger.error(f"Failed to load MCP tools: {str(e)}, using empty tool list")
mcp_tools = []
# 检测或使用指定的提供商 # 检测或使用指定的提供商
model_provider, base_url = detect_provider(config.model_name, config.model_server) model_provider, base_url = detect_provider(config.model_name, config.model_server)

View File

@ -114,7 +114,22 @@ async def enhanced_generate_stream_response(
await output_queue.put(("agent_done", None)) await output_queue.put(("agent_done", None))
except Exception as e: except Exception as e:
logger.error(f"Error in agent task: {e}") import traceback
error_details = traceback.format_exc()
logger.error(f"Error in agent task: {str(e)}")
logger.error(f"Full traceback: {error_details}")
# 发送错误信息给客户端
error_data = {
"error": {
"message": f"Agent execution failed: {str(e)}",
"type": "agent_error",
"details": error_details if __debug__ else str(e)
}
}
error_chunk = create_stream_chunk(f"chatcmpl-error", config.model_name, json.dumps(error_data, ensure_ascii=False))
await output_queue.put(("agent", f"data: {json.dumps(error_chunk, ensure_ascii=False)}\n\n"))
# 发送完成信号,确保输出控制器能正常结束
await output_queue.put(("agent_done", None)) await output_queue.put(("agent_done", None))
# 并发执行任务 # 并发执行任务
@ -186,6 +201,7 @@ async def enhanced_generate_stream_response(
} }
} }
yield f"data: {json.dumps(error_data, ensure_ascii=False)}\n\n" yield f"data: {json.dumps(error_data, ensure_ascii=False)}\n\n"
yield "data: [DONE]\n\n"
async def create_agent_and_generate_response( async def create_agent_and_generate_response(