|
|
@ -1,5 +1,7 @@ |
|
|
package com.yunqiinnovation.open_ai_service.mcp |
|
|
package com.yunqiinnovation.open_ai_service.mcp |
|
|
|
|
|
|
|
|
|
|
|
import android.os.Parcel |
|
|
|
|
|
import android.os.Parcelable |
|
|
import android.util.Log |
|
|
import android.util.Log |
|
|
import io.ktor.client.* |
|
|
import io.ktor.client.* |
|
|
import io.ktor.client.plugins.sse.* |
|
|
import io.ktor.client.plugins.sse.* |
|
|
@ -7,6 +9,7 @@ import io.ktor.client.request.* |
|
|
import io.ktor.client.statement.* |
|
|
import io.ktor.client.statement.* |
|
|
import io.ktor.http.* |
|
|
import io.ktor.http.* |
|
|
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage |
|
|
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage |
|
|
|
|
|
import io.modelcontextprotocol.kotlin.sdk.JSONRPCRequest |
|
|
import io.modelcontextprotocol.kotlin.sdk.shared.AbstractTransport |
|
|
import io.modelcontextprotocol.kotlin.sdk.shared.AbstractTransport |
|
|
import kotlinx.coroutines.* |
|
|
import kotlinx.coroutines.* |
|
|
import kotlinx.serialization.encodeToString |
|
|
import kotlinx.serialization.encodeToString |
|
|
@ -16,6 +19,40 @@ import kotlin.properties.Delegates |
|
|
import kotlin.time.Duration |
|
|
import kotlin.time.Duration |
|
|
import java.util.concurrent.atomic.AtomicBoolean |
|
|
import java.util.concurrent.atomic.AtomicBoolean |
|
|
import org.json.JSONObject |
|
|
import org.json.JSONObject |
|
|
|
|
|
import kotlinx.serialization.Serializable |
|
|
|
|
|
import kotlinx.serialization.json.JsonElement |
|
|
|
|
|
import kotlinx.serialization.json.JsonPrimitive |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//@Serializable |
|
|
|
|
|
//data class JSONRPCRequest( |
|
|
|
|
|
// val jsonrpc: String = "2.0", |
|
|
|
|
|
// val method: String, |
|
|
|
|
|
// val params: JsonElement? = null, |
|
|
|
|
|
// val id: JsonPrimitive? = null |
|
|
|
|
|
//) : JSONRPCMessage, Parcelable { |
|
|
|
|
|
// |
|
|
|
|
|
// constructor(parcel: Parcel) : this( |
|
|
|
|
|
// parcel.readString() ?: "2.0", |
|
|
|
|
|
// parcel.readString() ?: "", |
|
|
|
|
|
// parcel.readString()?.let { Json.decodeFromString<JsonElement>(it) }, |
|
|
|
|
|
// parcel.readString()?.let { Json.decodeFromString<JsonPrimitive>(it) } |
|
|
|
|
|
// ) |
|
|
|
|
|
// |
|
|
|
|
|
// override fun writeToParcel(parcel: Parcel, flags: Int) { |
|
|
|
|
|
// parcel.writeString(jsonrpc) |
|
|
|
|
|
// parcel.writeString(method) |
|
|
|
|
|
// parcel.writeString(params?.let { Json.encodeToString(it) }) |
|
|
|
|
|
// parcel.writeString(id?.let { Json.encodeToString(it) }) |
|
|
|
|
|
// } |
|
|
|
|
|
// |
|
|
|
|
|
// override fun describeContents(): Int = 0 |
|
|
|
|
|
// |
|
|
|
|
|
// companion object CREATOR : Parcelable.Creator<JSONRPCRequest> { |
|
|
|
|
|
// override fun createFromParcel(parcel: Parcel): JSONRPCRequest = JSONRPCRequest(parcel) |
|
|
|
|
|
// override fun newArray(size: Int): Array<JSONRPCRequest?> = arrayOfNulls(size) |
|
|
|
|
|
// } |
|
|
|
|
|
//} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 自定义SSE客户端传输层,修复原始SseClientTransport中的URL拼接问题 |
|
|
* 自定义SSE客户端传输层,修复原始SseClientTransport中的URL拼接问题 |
|
|
@ -40,6 +77,11 @@ class CustomSseClientTransport( |
|
|
|
|
|
|
|
|
private var job: Job? = null |
|
|
private var job: Job? = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val heartbeatIntervalMs = 30_000L // 30秒 |
|
|
|
|
|
private var heartbeatJob: Job? = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建JSON解析器,增强灵活性设置 |
|
|
// 创建JSON解析器,增强灵活性设置 |
|
|
private val json = Json { |
|
|
private val json = Json { |
|
|
ignoreUnknownKeys = true // 忽略未知字段 |
|
|
ignoreUnknownKeys = true // 忽略未知字段 |
|
|
@ -299,6 +341,9 @@ class CustomSseClientTransport( |
|
|
// 收集SSE事件 |
|
|
// 收集SSE事件 |
|
|
collectEvents() |
|
|
collectEvents() |
|
|
|
|
|
|
|
|
|
|
|
// 启动心跳任务 |
|
|
|
|
|
startHeartbeat() |
|
|
|
|
|
|
|
|
// 等待endpoint就绪 |
|
|
// 等待endpoint就绪 |
|
|
endpoint.await() |
|
|
endpoint.await() |
|
|
Log.d(TAG, "传输层启动完成,消息端点已就绪") |
|
|
Log.d(TAG, "传输层启动完成,消息端点已就绪") |
|
|
@ -346,7 +391,7 @@ class CustomSseClientTransport( |
|
|
Log.e(TAG, "关闭失败: 传输层未初始化") |
|
|
Log.e(TAG, "关闭失败: 传输层未初始化") |
|
|
error("CustomSseClientTransport is not initialized!") |
|
|
error("CustomSseClientTransport is not initialized!") |
|
|
} |
|
|
} |
|
|
|
|
|
heartbeatJob?.cancel() |
|
|
session.cancel() |
|
|
session.cancel() |
|
|
_onClose() |
|
|
_onClose() |
|
|
job?.cancelAndJoin() |
|
|
job?.cancelAndJoin() |
|
|
@ -359,4 +404,29 @@ class CustomSseClientTransport( |
|
|
fun isInitialized(): Boolean { |
|
|
fun isInitialized(): Boolean { |
|
|
return initialized.get() |
|
|
return initialized.get() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun startHeartbeat() { |
|
|
|
|
|
heartbeatJob = scope.launch { |
|
|
|
|
|
while (isActive) { |
|
|
|
|
|
delay(heartbeatIntervalMs) |
|
|
|
|
|
try { |
|
|
|
|
|
sendHeartbeat() |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(TAG, "心跳发送失败: ${e.message}", e) |
|
|
|
|
|
_onError(e) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private suspend fun sendHeartbeat() { |
|
|
|
|
|
val pingMessage = JSONRPCRequest( |
|
|
|
|
|
method = "ping", |
|
|
|
|
|
jsonrpc = "2.0" |
|
|
|
|
|
) |
|
|
|
|
|
send(pingMessage) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|