diff --git a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt index c05746d..da2d5ab 100644 --- a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt +++ b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt @@ -359,7 +359,27 @@ class ChatConversation( private fun newId(prefix: String): String = "$prefix-${java.util.UUID.randomUUID()}" - private fun encodeArgsJson(arguments: Map): String = encodeToolArgs(arguments) + private fun encodeArgsJson(arguments: Map): String { + val el = kotlinx.serialization.json.JsonElement.serializer() + val obj = kotlinx.serialization.json.buildJsonObject { + arguments.forEach { (k, v) -> put(k, v.toJsonElement()) } + } + return kotlinx.serialization.json.Json.encodeToString(el, obj) + } + + private fun Any?.toJsonElement(): kotlinx.serialization.json.JsonElement = when (this) { + null -> kotlinx.serialization.json.JsonNull + is Boolean -> kotlinx.serialization.json.JsonPrimitive(this) + is Number -> kotlinx.serialization.json.JsonPrimitive(this) + is String -> kotlinx.serialization.json.JsonPrimitive(this) + is Map<*, *> -> kotlinx.serialization.json.buildJsonObject { + this@toJsonElement.forEach { (k, v) -> + put(k.toString(), v.toJsonElement()) + } + } + is List<*> -> kotlinx.serialization.json.JsonArray(this.map { it.toJsonElement() }) + else -> kotlinx.serialization.json.JsonPrimitive(toString()) + } companion object { private const val MAX_TOOL_LOOPS = 16 diff --git a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/mcp/McpRegistry.kt b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/mcp/McpRegistry.kt index 020800f..1fde3b4 100644 --- a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/mcp/McpRegistry.kt +++ b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/mcp/McpRegistry.kt @@ -22,7 +22,11 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.booleanOrNull import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.doubleOrNull +import kotlinx.serialization.json.intOrNull +import kotlinx.serialization.json.longOrNull import kotlinx.serialization.json.put import pw.binom.agentik.standalone.agent.NamedTool import pw.binom.litert.LiteTool @@ -234,10 +238,22 @@ internal class McpLiteToolAdapter( private fun JsonObject.toAnyMap(): Map = entries.associate { (k, v) -> k to jsonElementToAny(v) } + /** + * Преобразует [JsonPrimitive] в типизированное значение (Boolean/Int/Long/Float/Double), + * и только в крайнем случае — в String. Без этого MCP-сервер получает все аргументы + * как строки и отвергает их по JSON-schema (например, `max_length:500` → `"500"` → + * «'500' is not of type 'integer'»). + */ private fun jsonElementToAny(el: JsonElement): Any? = when (el) { - is JsonPrimitive -> el.content + is JsonPrimitive -> + el.booleanOrNull + ?: el.intOrNull + ?: el.longOrNull + ?: el.doubleOrNull + ?: if (el.isString) el.content else el.content is JsonArray -> el.map { jsonElementToAny(it) } is JsonObject -> el.toAnyMap() + else -> null } } }