import Foundation /// 工具类型枚举 public enum ToolType { case localFunction // 本地函数 case mcpTool // MCP工具 } /// 函数处理器协议 public protocol FunctionHandler { /// 处理函数调用 /// - Parameter arguments: 函数参数,字典格式 /// - Returns: 函数执行结果,字符串 func handle(arguments: [String: Any]) async throws -> String } /// 单个MCP子客户端 /// 负责连接到一个具体的MCP服务器并管理其工具 public class MCPSubClient { private let tag: String private let serverId: String private let serverUrl: String // 服务器提供的工具列表 private var tools: [[String: Any]] = [] // 工具Map列表,用于OpenAI请求 private var toolMaps: [[String: Any]] = [] // 是否已连接 private var isConnectedFlag = false // 工具名称到工具的映射,用于快速查找 private var toolMapping: [String: [String: Any]] = [:] // URL会话 private lazy var urlSession: URLSession = { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 30 config.timeoutIntervalForResource = 30 return URLSession(configuration: config) }() public init(serverId: String, serverUrl: String) { self.serverId = serverId self.serverUrl = serverUrl self.tag = "MCPSubClient[\(serverId)]" print("\(tag) 初始化,URL: \(serverUrl)") } /// 连接到MCP服务器(通过SSE) /// - Returns: 是否连接成功 public func connect() async -> Bool { do { print("\(tag) 开始连接服务器: \(serverUrl)") // 这里应该实现实际的SSE连接逻辑 // 由于iOS没有直接的SSE库,我们可以使用URLSession来模拟 // 或者使用第三方库如EventSource // 模拟连接成功并获取工具列表 await processToolsList() isConnectedFlag = true print("\(tag) 连接成功,可用工具: \(tools.count)个") return true } catch { print("\(tag) 连接失败: \(error.localizedDescription)") return false } } /// 处理工具列表 private func processToolsList() async { // 这里应该从MCP服务器获取实际的工具列表 // 目前先模拟一些基本工具 tools = [] // 更新工具映射 toolMapping.removeAll() for tool in tools { if let name = tool["name"] as? String { toolMapping[name] = tool } } // 更新工具Map列表 updateToolMaps() print("\(tag) 成功处理 \(tools.count) 个工具") } /// 更新工具Map列表,用于OpenAI请求 /// 将MCP工具转换为OpenAI函数调用格式 private func updateToolMaps() { toolMaps.removeAll() for tool in tools { guard let name = tool["name"] as? String, let description = tool["description"] as? String else { continue } let functionMap: [String: Any] = [ "name": name, "description": description, "parameters": [ "type": "object", "properties": [:], "required": [] ] ] toolMaps.append([ "type": "function", "function": functionMap ]) } print("\(tag) 更新工具映射完成,共 \(toolMaps.count) 个工具") } /// 获取工具Map列表,用于OpenAI请求 public func getToolMaps() -> [[String: Any]] { return toolMaps } /// 获取可用的工具列表 public func getTools() -> [[String: Any]] { return tools } /// 检查是否包含指定工具 public func containsTool(name: String) -> Bool { return toolMapping.keys.contains(name) } /// 调用工具 public func callTool(name: String, arguments: [String: Any]) async throws -> [String: Any] { guard isConnectedFlag else { print("\(tag) 未连接到服务器") return ["context": "未连接到MCP服务器"] } guard containsTool(name: name) else { print("\(tag) 此客户端不包含工具: \(name)") throw OpenAIException("工具不存在: \(name)") } // 这里应该实现实际的工具调用逻辑 // 目前先返回模拟结果 return [ "name": name, "context": "工具调用结果: \(name)" ] } /// 检查是否已连接 public func isConnected() -> Bool { return isConnectedFlag } /// 关闭连接 public func close() { isConnectedFlag = false print("\(tag) 已关闭MCP服务器连接") } } /// MCP客户端,用于连接Model Context Protocol服务器和管理本地函数 public class MCPClient { private let tag = "MCPClient" // 本地函数Map,函数名 -> 处理器 private var localFunctions: [String: FunctionHandler] = [:] // 本地函数定义Map,函数名 -> 定义 private var localFunctionDefs: [String: String] = [:] // 子客户端列表,每个连接一个MCP服务器 private var subClients: [String: MCPSubClient] = [:] // 是否已连接 private var isConnectedFlag = false // 系统功能处理器 private var systemFunctionHandler: SystemFunctionHandler? public init() { // 初始化系统功能处理器 systemFunctionHandler = SystemFunctionHandler() initializeVoiceFunctions() } /// 初始化语音功能处理器 private func initializeVoiceFunctions() { do { // 使用SystemFunctionHandler注册所有功能 systemFunctionHandler?.registerAllFunctions(client: self) print("\(tag) 语音功能初始化完成") } catch { print("\(tag) 语音功能初始化失败: \(error.localizedDescription)") } } /// 连接到MCP服务器(通过SSE) /// 直接接收完整的JSON配置字符串 /// - Parameter mcpConfigJson: 包含mcpServers字段的JSON配置字符串 /// - Returns: 是否连接成功 public func connectToSSE(mcpConfigJson: String) async throws -> Bool { // 清除现有连接 closeAllConnections() var serverMap: [String: String] = [:] var configLoaded = false // 尝试解析传入的JSON配置 if !mcpConfigJson.isEmpty { do { guard let data = mcpConfigJson.data(using: .utf8), let config = try JSONSerialization.jsonObject(with: data) as? [String: Any], let mcpServers = config["mcpServers"] as? [String: [String: String]] else { throw OpenAIException("无效的MCP配置格式") } // 从服务器配置中提取URL for (serverId, serverConfig) in mcpServers { if let url = serverConfig["url"], !url.isEmpty { serverMap[serverId] = url print("\(tag) 从配置中添加服务器 [\(serverId)]: \(url)") } } configLoaded = !serverMap.isEmpty } catch { print("\(tag) 解析MCP配置失败: \(error.localizedDescription)") } } // 如果没有从配置中加载到服务器,使用默认配置 if !configLoaded { print("\(tag) 使用默认MCP服务器配置") // 可以在这里添加默认的服务器配置 } // 连接到所有服务器 var successCount = 0 for (serverId, serverUrl) in serverMap { let subClient = MCPSubClient(serverId: serverId, serverUrl: serverUrl) let connected = await subClient.connect() if connected { subClients[serverId] = subClient successCount += 1 print("\(tag) 成功连接到服务器 [\(serverId)]") } else { print("\(tag) 连接服务器失败 [\(serverId)]") } } isConnectedFlag = successCount > 0 print("\(tag) MCP客户端连接完成,成功连接 \(successCount)/\(serverMap.count) 个服务器") return isConnectedFlag } /// 关闭所有连接 private func closeAllConnections() { for (_, subClient) in subClients { subClient.close() } subClients.removeAll() isConnectedFlag = false } /// 获取工具Map列表,用于OpenAI请求 public func getToolMaps() -> [[String: Any]] { var allToolMaps: [[String: Any]] = [] // 添加所有子客户端的工具 for (_, subClient) in subClients { allToolMaps.append(contentsOf: subClient.getToolMaps()) } // 添加本地函数 for (name, _) in localFunctions { if let def = localFunctionDefs[name], let data = def.data(using: .utf8), let functionDef = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { allToolMaps.append([ "type": "function", "function": functionDef ]) } } return allToolMaps } /// 注册本地函数 public func registerLocalFunction(name: String, description: String, parameters: [String: Any], handler: FunctionHandler) -> Bool { do { localFunctions[name] = handler let functionDef: [String: Any] = [ "name": name, "description": description, "parameters": parameters ] let jsonData = try JSONSerialization.data(withJSONObject: functionDef) let jsonString = String(data: jsonData, encoding: .utf8) ?? "" localFunctionDefs[name] = jsonString // print("\(tag) 成功注册本地函数: \(name)") return true } catch { print("\(tag) 注册本地函数失败: \(error.localizedDescription)") return false } } /// 调用工具 public func callTool(name: String, arguments: [String: Any]) async throws -> [String: Any] { // 首先检查是否是本地函数 if let handler = localFunctions[name] { do { let result = try await handler.handle(arguments: arguments) return ["context": result] } catch { throw OpenAIException("本地函数调用失败: \(error.localizedDescription)") } } // 然后检查MCP工具 for (_, subClient) in subClients { if subClient.containsTool(name: name) { return try await subClient.callTool(name: name, arguments: arguments) } } throw OpenAIException("未找到工具: \(name)") } /// 解析JSON参数 public func parseJsonArguments(_ argumentsJson: String) throws -> [String: Any] { guard let data = argumentsJson.data(using: .utf8), let arguments = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return [:] } return arguments } /// 检查是否已连接 public func isConnected() -> Bool { return isConnectedFlag || !localFunctions.isEmpty } /// 关闭连接 public func close() { closeAllConnections() localFunctions.removeAll() localFunctionDefs.removeAll() print("\(tag) 已关闭MCP客户端") } }