|
|
|
@ -28,6 +28,7 @@ public protocol FunctionHandler { |
|
|
|
/// - 安全的JSON-RPC消息解析,包含错误修复逻辑 |
|
|
|
/// - 完整的SSE连接生命周期管理 |
|
|
|
/// - 与Android端CustomSseClientTransport功能对等 |
|
|
|
// 在 MCPSubClient 类中添加工具调用超时和重试配置 |
|
|
|
public class MCPSubClient { |
|
|
|
private let serverId: String |
|
|
|
private let serverUrl: String |
|
|
|
@ -47,6 +48,10 @@ public class MCPSubClient { |
|
|
|
private var retryCount = 0 |
|
|
|
private var reconnectTask: Task<Void, Never>? |
|
|
|
|
|
|
|
// 工具调用配置 - 添加这两行 |
|
|
|
private let maxToolCallRetries = 3 |
|
|
|
private let toolCallTimeout: TimeInterval = 30.0 |
|
|
|
|
|
|
|
// 连接状态锁 |
|
|
|
private let connectionLock = NSLock() |
|
|
|
|
|
|
|
@ -178,27 +183,55 @@ public class MCPSubClient { |
|
|
|
|
|
|
|
/// 调用工具前检查连接 |
|
|
|
public func callTool(name: String, arguments: [String: Any]) async -> [String: Any]? { |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: 0) |
|
|
|
} |
|
|
|
|
|
|
|
private func callToolWithRetry(name: String, arguments: [String: Any], retryCount: Int) async -> [String: Any]? { |
|
|
|
print("[MCPSubClient] 工具调用尝试 \(retryCount + 1)/\(maxToolCallRetries + 1): \(name)") |
|
|
|
|
|
|
|
// 先检查连接状态 |
|
|
|
if !(await checkConnection()) { |
|
|
|
if retryCount < maxToolCallRetries { |
|
|
|
print("[MCPSubClient] 连接断开,尝试重连...") |
|
|
|
if await connect() { |
|
|
|
print("[MCPSubClient] 重连成功,重试工具调用") |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1) |
|
|
|
} |
|
|
|
} |
|
|
|
return [ |
|
|
|
"content": [[ |
|
|
|
"type": "text", |
|
|
|
"text": "Tool call failed: mcp: service not connected" |
|
|
|
"text": "工具调用失败,请重试:MCP 服务连接中断" |
|
|
|
]], |
|
|
|
"isError": true |
|
|
|
] |
|
|
|
} |
|
|
|
|
|
|
|
guard isConnectedFlag, let client = mcpClient else { |
|
|
|
return createErrorResult("Not connected to MCP server") |
|
|
|
if retryCount < maxToolCallRetries { |
|
|
|
print("[MCPSubClient] MCP 客户端未连接,尝试重连...") |
|
|
|
if await connect() { |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1) |
|
|
|
} |
|
|
|
} |
|
|
|
return createErrorResult("工具调用失败,请重试:MCP 服务未连接") |
|
|
|
} |
|
|
|
|
|
|
|
do { |
|
|
|
let mcpArguments = convertToMCPValue(arguments) |
|
|
|
let (content, isError) = try await client.callTool(name: name, arguments: mcpArguments) |
|
|
|
// 使用超时控制的工具调用 |
|
|
|
let result = try await withTimeout(seconds: toolCallTimeout) { |
|
|
|
let mcpArguments = self.convertToMCPValue(arguments) |
|
|
|
return try await client.callTool(name: name, arguments: mcpArguments) |
|
|
|
} |
|
|
|
|
|
|
|
let (content, isError) = result |
|
|
|
|
|
|
|
if isError == true { |
|
|
|
return createErrorResult("Tool execution failed") |
|
|
|
if retryCount < maxToolCallRetries { |
|
|
|
print("[MCPSubClient] 工具执行失败,重试中...") |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1) |
|
|
|
} |
|
|
|
return createErrorResult("工具调用失败,请重试:工具执行错误") |
|
|
|
} |
|
|
|
|
|
|
|
var contextText = "" |
|
|
|
@ -225,13 +258,45 @@ public class MCPSubClient { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
print("[MCPSubClient] 工具调用成功: \(name)") |
|
|
|
return [ |
|
|
|
"content": [["type": "text", "text": contextText.trimmingCharacters(in: .whitespacesAndNewlines)]], |
|
|
|
"isError": false |
|
|
|
] |
|
|
|
|
|
|
|
} catch is TimeoutError { |
|
|
|
print("[MCPSubClient] 工具调用超时: \(name)") |
|
|
|
if retryCount < maxToolCallRetries { |
|
|
|
print("[MCPSubClient] 超时重试中...") |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1) |
|
|
|
} |
|
|
|
return createErrorResult("工具调用失败,请重试:连接超时") |
|
|
|
} catch { |
|
|
|
return createErrorResult("Tool call failed: \(error.localizedDescription)") |
|
|
|
print("[MCPSubClient] 工具调用异常: \(name), 错误: \(error.localizedDescription)") |
|
|
|
|
|
|
|
// 检查是否是连接相关错误 |
|
|
|
let errorDescription = error.localizedDescription.lowercased() |
|
|
|
if errorDescription.contains("connection") || |
|
|
|
errorDescription.contains("network") || |
|
|
|
errorDescription.contains("timeout") || |
|
|
|
errorDescription.contains("disconnected") { |
|
|
|
|
|
|
|
if retryCount < maxToolCallRetries { |
|
|
|
print("[MCPSubClient] 检测到连接错误,尝试重连并重试...") |
|
|
|
// 标记连接断开 |
|
|
|
isConnectedFlag = false |
|
|
|
|
|
|
|
// 尝试重连 |
|
|
|
if await connect() { |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1) |
|
|
|
} |
|
|
|
} |
|
|
|
} else if retryCount < maxToolCallRetries { |
|
|
|
print("[MCPSubClient] 其他错误,直接重试...") |
|
|
|
return await callToolWithRetry(name: name, arguments: arguments, retryCount: retryCount + 1) |
|
|
|
} |
|
|
|
|
|
|
|
return createErrorResult("工具调用失败,请重试:\(error.localizedDescription)") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|