From a96995f84abd2b27649088693ce0ae9db897d3e3 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 9 May 2025 16:47:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=20mcp=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=A7=A3=E6=9E=90bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../open_ai_service/MCPClient.kt | 99 ++++++++++--------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt b/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt index dce107f70..6460b09d6 100644 --- a/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt +++ b/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt @@ -199,88 +199,69 @@ class MCPSubClient( */ private fun updateToolMaps() { toolMaps.clear() - + try { - // 转换MCP工具为OpenAI函数格式 tools.forEach { toolUnion -> val tool = toolUnion.tool().getOrNull() ?: return@forEach - + try { - // 获取工具的输入模式 val inputSchema = tool.inputSchema() ?: return@forEach - - // 使用toString()获取完整JSON字符串 val schemaStr = inputSchema.toString() Log.d(TAG, "工具 ${tool.name()} 的Schema: $schemaStr") - - // 解析properties和required + val propertiesMap = HashMap() val requiredList = ArrayList() - + try { - // 匹配properties内部的内容 - val propertiesPattern = "properties=\\{(.*?)\\}(?:,|\\s|$)".toRegex(RegexOption.DOT_MATCHES_ALL) - val propertiesMatch = propertiesPattern.find(schemaStr) - - if (propertiesMatch != null) { - val propertiesContent = propertiesMatch.groupValues[1] - - // 匹配每个属性字段,例如 query={description=搜索信息, type=string} + // ✅ 替换原来的正则方式,使用括号匹配方式提取 properties 内容 + val propertiesContent = extractPropertiesContent(schemaStr) + if (propertiesContent != null) { val fieldPattern = "([a-zA-Z0-9_]+)=\\{([^{}]+?)\\}".toRegex() val fieldMatches = fieldPattern.findAll(propertiesContent) - + fieldMatches.forEach { fieldMatch -> val fieldName = fieldMatch.groupValues[1] val fieldContent = fieldMatch.groupValues[2] - - // 解析字段属性 + val fieldMap = HashMap() - - // 解析如 description=搜索信息, type=string 格式的键值对 - // 同时处理带引号和不带引号的情况 val keyValuePattern = "([a-zA-Z0-9_]+)=(\"[^\"]*\"|'[^']*'|[^,}\\s]+)".toRegex() val keyValueMatches = keyValuePattern.findAll(fieldContent) - + keyValueMatches.forEach { kvMatch -> val key = kvMatch.groupValues[1] var value = kvMatch.groupValues[2].trim() - - // 去除引号(如果有) - if ((value.startsWith("\"") && value.endsWith("\"")) || + + if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) { value = value.substring(1, value.length - 1) } - - // 处理不同类型的值 + val processedValue: Any = when { value.equals("true", ignoreCase = true) -> true value.equals("false", ignoreCase = true) -> false value.matches("\\d+".toRegex()) -> value.toIntOrNull() ?: value value.matches("\\d+\\.\\d+".toRegex()) -> value.toDoubleOrNull() ?: value - else -> value // 保持为字符串 + else -> value } - + fieldMap[key] = processedValue } - - // 添加到属性映射 + propertiesMap[fieldName] = fieldMap } - + Log.d(TAG, "成功解析属性: $propertiesMap") } - - // 解析required + + // ✅ required 部分保持不变 val requiredPattern = "required=\\[(.*?)\\]".toRegex() val requiredMatch = requiredPattern.find(schemaStr) - if (requiredMatch != null) { val requiredStr = requiredMatch.groupValues[1] if (requiredStr.isNotEmpty()) { - // 处理可能带引号的字段名 - val fieldNames = requiredStr.split(",").map { + val fieldNames = requiredStr.split(",").map { val trimmed = it.trim() - if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) || + if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { trimmed.substring(1, trimmed.length - 1) } else { @@ -295,8 +276,7 @@ class MCPSubClient( Log.e(TAG, "解析Schema字符串失败: ${e.message}", e) Log.e(TAG, "原始Schema: $schemaStr") } - - // 创建OpenAI函数格式 + val functionMap = mapOf( "name" to tool.name(), "description" to (tool.description() ?: ""), @@ -306,13 +286,12 @@ class MCPSubClient( "required" to requiredList ) ) - - // 添加到工具列表 + toolMaps.add(mapOf( "type" to "function", "function" to functionMap )) - + Log.d(TAG, "成功转换工具 ${tool.name()} 到OpenAI函数格式 $functionMap") } catch (e: Exception) { Log.e(TAG, "转换工具 ${tool.name()} 失败: ${e.message}") @@ -321,10 +300,36 @@ class MCPSubClient( } catch (e: Exception) { Log.e(TAG, "更新工具映射失败: ${e.message}") } - + Log.d(TAG, "更新工具映射完成,共 ${toolMaps.size} 个工具") } - + + private fun extractPropertiesContent(schemaStr: String): String? { + val startKey = "properties={" + val startIndex = schemaStr.indexOf(startKey) + if (startIndex == -1) return null + + var braceCount = 0 + var endIndex = -1 + val contentStart = startIndex + startKey.length + + for (i in contentStart until schemaStr.length) { + when (schemaStr[i]) { + '{' -> braceCount++ + '}' -> { + if (braceCount == 0) { + endIndex = i + break + } else { + braceCount-- + } + } + } + } + + return if (endIndex != -1) schemaStr.substring(contentStart, endIndex) else null + } + /** * 将JsonNode转换为Map */