Add ktlint (#213)
* Add ktlint * Simplify `initClient` function and clean imports in SseIntegrationTest.
This commit is contained in:
parent
b0f075900a
commit
91cf1205af
40 changed files with 800 additions and 758 deletions
37
.editorconfig
Normal file
37
.editorconfig
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
max_line_length = 120
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{*.yaml,*.yml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||||
|
|
||||||
|
# Disable wildcard imports entirely
|
||||||
|
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||||
|
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||||
|
ij_kotlin_packages_to_use_import_on_demand = unset
|
||||||
|
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_experimental = enabled
|
||||||
|
ktlint_standard_filename = disabled
|
||||||
|
ktlint_standard_no-empty-first-line-in-class-body = disabled
|
||||||
|
ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 4
|
||||||
|
ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 4
|
||||||
|
ktlint_standard_chain-method-continuation = disabled
|
||||||
|
ktlint_ignore_back_ticked_identifier = true
|
||||||
|
ktlint_standard_multiline-expression-wrapping = disabled
|
||||||
|
ktlint_standard_when-entry-bracing = disabled
|
||||||
|
|
||||||
|
[*/build/**/*]
|
||||||
|
ktlint = disabled
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.ktlint)
|
||||||
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "io.modelcontextprotocol"
|
group = "io.modelcontextprotocol"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply(plugin = "org.jlleitschuh.gradle.ktlint")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
kotlin = "2.2.0"
|
kotlin = "2.2.0"
|
||||||
dokka = "2.0.0"
|
dokka = "2.0.0"
|
||||||
atomicfu = "0.29.0"
|
atomicfu = "0.29.0"
|
||||||
|
ktlint = "13.0.0"
|
||||||
|
|
||||||
# libraries version
|
# libraries version
|
||||||
serialization = "1.9.0"
|
serialization = "1.9.0"
|
||||||
|
|
@ -57,6 +58,7 @@ ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serializatio
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
kotlinx-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidatorPlugin" }
|
kotlinx-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidatorPlugin" }
|
||||||
|
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
|
||||||
|
|
||||||
# Samples
|
# Samples
|
||||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,15 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
iosArm64(); iosX64(); iosSimulatorArm64()
|
iosArm64()
|
||||||
watchosX64(); watchosArm64(); watchosSimulatorArm64()
|
iosX64()
|
||||||
tvosX64(); tvosArm64(); tvosSimulatorArm64()
|
iosSimulatorArm64()
|
||||||
|
watchosX64()
|
||||||
|
watchosArm64()
|
||||||
|
watchosSimulatorArm64()
|
||||||
|
tvosX64()
|
||||||
|
tvosArm64()
|
||||||
|
tvosSimulatorArm64()
|
||||||
js {
|
js {
|
||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
|
|
@ -40,4 +46,4 @@ kotlin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,8 @@ public class ClientOptions(
|
||||||
* @param clientInfo Information about the client implementation (name, version).
|
* @param clientInfo Information about the client implementation (name, version).
|
||||||
* @param options Configuration options for this client.
|
* @param options Configuration options for this client.
|
||||||
*/
|
*/
|
||||||
public open class Client(
|
public open class Client(private val clientInfo: Implementation, options: ClientOptions = ClientOptions()) :
|
||||||
private val clientInfo: Implementation,
|
Protocol(options) {
|
||||||
options: ClientOptions = ClientOptions(),
|
|
||||||
) : Protocol(options) {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the server's reported capabilities after the initialization process completes.
|
* Retrieves the server's reported capabilities after the initialization process completes.
|
||||||
|
|
@ -144,13 +142,13 @@ public open class Client(
|
||||||
val message = InitializeRequest(
|
val message = InitializeRequest(
|
||||||
protocolVersion = LATEST_PROTOCOL_VERSION,
|
protocolVersion = LATEST_PROTOCOL_VERSION,
|
||||||
capabilities = capabilities,
|
capabilities = capabilities,
|
||||||
clientInfo = clientInfo
|
clientInfo = clientInfo,
|
||||||
)
|
)
|
||||||
val result = request<InitializeResult>(message)
|
val result = request<InitializeResult>(message)
|
||||||
|
|
||||||
if (!SUPPORTED_PROTOCOL_VERSIONS.contains(result.protocolVersion)) {
|
if (!SUPPORTED_PROTOCOL_VERSIONS.contains(result.protocolVersion)) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
"Server's protocol version is not supported: ${result.protocolVersion}"
|
"Server's protocol version is not supported: ${result.protocolVersion}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,11 +163,9 @@ public open class Client(
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun assertCapabilityForMethod(method: Method) {
|
override fun assertCapabilityForMethod(method: Method) {
|
||||||
when (method) {
|
when (method) {
|
||||||
Method.Defined.LoggingSetLevel -> {
|
Method.Defined.LoggingSetLevel -> {
|
||||||
|
|
@ -181,7 +177,7 @@ public open class Client(
|
||||||
Method.Defined.PromptsGet,
|
Method.Defined.PromptsGet,
|
||||||
Method.Defined.PromptsList,
|
Method.Defined.PromptsList,
|
||||||
Method.Defined.CompletionComplete,
|
Method.Defined.CompletionComplete,
|
||||||
-> {
|
-> {
|
||||||
if (serverCapabilities?.prompts == null) {
|
if (serverCapabilities?.prompts == null) {
|
||||||
throw IllegalStateException("Server does not support prompts (required for $method)")
|
throw IllegalStateException("Server does not support prompts (required for $method)")
|
||||||
}
|
}
|
||||||
|
|
@ -192,20 +188,20 @@ public open class Client(
|
||||||
Method.Defined.ResourcesRead,
|
Method.Defined.ResourcesRead,
|
||||||
Method.Defined.ResourcesSubscribe,
|
Method.Defined.ResourcesSubscribe,
|
||||||
Method.Defined.ResourcesUnsubscribe,
|
Method.Defined.ResourcesUnsubscribe,
|
||||||
-> {
|
-> {
|
||||||
val resCaps = serverCapabilities?.resources
|
val resCaps = serverCapabilities?.resources
|
||||||
?: error("Server does not support resources (required for $method)")
|
?: error("Server does not support resources (required for $method)")
|
||||||
|
|
||||||
if (method == Method.Defined.ResourcesSubscribe && resCaps.subscribe != true) {
|
if (method == Method.Defined.ResourcesSubscribe && resCaps.subscribe != true) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
"Server does not support resource subscriptions (required for $method)"
|
"Server does not support resource subscriptions (required for $method)",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Method.Defined.ToolsCall,
|
Method.Defined.ToolsCall,
|
||||||
Method.Defined.ToolsList,
|
Method.Defined.ToolsList,
|
||||||
-> {
|
-> {
|
||||||
if (serverCapabilities?.tools == null) {
|
if (serverCapabilities?.tools == null) {
|
||||||
throw IllegalStateException("Server does not support tools (required for $method)")
|
throw IllegalStateException("Server does not support tools (required for $method)")
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +209,7 @@ public open class Client(
|
||||||
|
|
||||||
Method.Defined.Initialize,
|
Method.Defined.Initialize,
|
||||||
Method.Defined.Ping,
|
Method.Defined.Ping,
|
||||||
-> {
|
-> {
|
||||||
// No specific capability required
|
// No specific capability required
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +224,7 @@ public open class Client(
|
||||||
Method.Defined.NotificationsRootsListChanged -> {
|
Method.Defined.NotificationsRootsListChanged -> {
|
||||||
if (capabilities.roots?.listChanged != true) {
|
if (capabilities.roots?.listChanged != true) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
"Client does not support roots list changed notifications (required for $method)"
|
"Client does not support roots list changed notifications (required for $method)",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +232,7 @@ public open class Client(
|
||||||
Method.Defined.NotificationsInitialized,
|
Method.Defined.NotificationsInitialized,
|
||||||
Method.Defined.NotificationsCancelled,
|
Method.Defined.NotificationsCancelled,
|
||||||
Method.Defined.NotificationsProgress,
|
Method.Defined.NotificationsProgress,
|
||||||
-> {
|
-> {
|
||||||
// Always allowed
|
// Always allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +247,7 @@ public open class Client(
|
||||||
Method.Defined.SamplingCreateMessage -> {
|
Method.Defined.SamplingCreateMessage -> {
|
||||||
if (capabilities.sampling == null) {
|
if (capabilities.sampling == null) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
"Client does not support sampling capability (required for $method)"
|
"Client does not support sampling capability (required for $method)",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +255,7 @@ public open class Client(
|
||||||
Method.Defined.RootsList -> {
|
Method.Defined.RootsList -> {
|
||||||
if (capabilities.roots == null) {
|
if (capabilities.roots == null) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
"Client does not support roots capability (required for $method)"
|
"Client does not support roots capability (required for $method)",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +263,7 @@ public open class Client(
|
||||||
Method.Defined.ElicitationCreate -> {
|
Method.Defined.ElicitationCreate -> {
|
||||||
if (capabilities.elicitation == null) {
|
if (capabilities.elicitation == null) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
"Client does not support elicitation capability (required for $method)"
|
"Client does not support elicitation capability (required for $method)",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -280,16 +276,14 @@ public open class Client(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a ping request to the server to check connectivity.
|
* Sends a ping request to the server to check connectivity.
|
||||||
*
|
*
|
||||||
* @param options Optional request options.
|
* @param options Optional request options.
|
||||||
* @throws IllegalStateException If the server does not support the ping method (unlikely).
|
* @throws IllegalStateException If the server does not support the ping method (unlikely).
|
||||||
*/
|
*/
|
||||||
public suspend fun ping(options: RequestOptions? = null): EmptyRequestResult {
|
public suspend fun ping(options: RequestOptions? = null): EmptyRequestResult =
|
||||||
return request<EmptyRequestResult>(PingRequest(), options)
|
request<EmptyRequestResult>(PingRequest(), options)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a completion request to the server, typically to generate or complete some content.
|
* Sends a completion request to the server, typically to generate or complete some content.
|
||||||
|
|
@ -299,9 +293,8 @@ public open class Client(
|
||||||
* @return The completion result returned by the server, or `null` if none.
|
* @return The completion result returned by the server, or `null` if none.
|
||||||
* @throws IllegalStateException If the server does not support prompts or completion.
|
* @throws IllegalStateException If the server does not support prompts or completion.
|
||||||
*/
|
*/
|
||||||
public suspend fun complete(params: CompleteRequest, options: RequestOptions? = null): CompleteResult {
|
public suspend fun complete(params: CompleteRequest, options: RequestOptions? = null): CompleteResult =
|
||||||
return request(params, options)
|
request(params, options)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the logging level on the server.
|
* Sets the logging level on the server.
|
||||||
|
|
@ -310,9 +303,8 @@ public open class Client(
|
||||||
* @param options Optional request options.
|
* @param options Optional request options.
|
||||||
* @throws IllegalStateException If the server does not support logging.
|
* @throws IllegalStateException If the server does not support logging.
|
||||||
*/
|
*/
|
||||||
public suspend fun setLoggingLevel(level: LoggingLevel, options: RequestOptions? = null): EmptyRequestResult {
|
public suspend fun setLoggingLevel(level: LoggingLevel, options: RequestOptions? = null): EmptyRequestResult =
|
||||||
return request<EmptyRequestResult>(SetLevelRequest(level), options)
|
request<EmptyRequestResult>(SetLevelRequest(level), options)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a prompt by name from the server.
|
* Retrieves a prompt by name from the server.
|
||||||
|
|
@ -322,9 +314,8 @@ public open class Client(
|
||||||
* @return The requested prompt details, or `null` if not found.
|
* @return The requested prompt details, or `null` if not found.
|
||||||
* @throws IllegalStateException If the server does not support prompts.
|
* @throws IllegalStateException If the server does not support prompts.
|
||||||
*/
|
*/
|
||||||
public suspend fun getPrompt(request: GetPromptRequest, options: RequestOptions? = null): GetPromptResult {
|
public suspend fun getPrompt(request: GetPromptRequest, options: RequestOptions? = null): GetPromptResult =
|
||||||
return request(request, options)
|
request(request, options)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all available prompts from the server.
|
* Lists all available prompts from the server.
|
||||||
|
|
@ -337,9 +328,7 @@ public open class Client(
|
||||||
public suspend fun listPrompts(
|
public suspend fun listPrompts(
|
||||||
request: ListPromptsRequest = ListPromptsRequest(),
|
request: ListPromptsRequest = ListPromptsRequest(),
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): ListPromptsResult {
|
): ListPromptsResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all available resources from the server.
|
* Lists all available resources from the server.
|
||||||
|
|
@ -352,9 +341,7 @@ public open class Client(
|
||||||
public suspend fun listResources(
|
public suspend fun listResources(
|
||||||
request: ListResourcesRequest = ListResourcesRequest(),
|
request: ListResourcesRequest = ListResourcesRequest(),
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): ListResourcesResult {
|
): ListResourcesResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists resource templates available on the server.
|
* Lists resource templates available on the server.
|
||||||
|
|
@ -367,9 +354,7 @@ public open class Client(
|
||||||
public suspend fun listResourceTemplates(
|
public suspend fun listResourceTemplates(
|
||||||
request: ListResourceTemplatesRequest,
|
request: ListResourceTemplatesRequest,
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): ListResourceTemplatesResult {
|
): ListResourceTemplatesResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a resource from the server by its URI.
|
* Reads a resource from the server by its URI.
|
||||||
|
|
@ -382,9 +367,7 @@ public open class Client(
|
||||||
public suspend fun readResource(
|
public suspend fun readResource(
|
||||||
request: ReadResourceRequest,
|
request: ReadResourceRequest,
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): ReadResourceResult {
|
): ReadResourceResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to resource changes on the server.
|
* Subscribes to resource changes on the server.
|
||||||
|
|
@ -396,9 +379,7 @@ public open class Client(
|
||||||
public suspend fun subscribeResource(
|
public suspend fun subscribeResource(
|
||||||
request: SubscribeRequest,
|
request: SubscribeRequest,
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): EmptyRequestResult {
|
): EmptyRequestResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribes from resource changes on the server.
|
* Unsubscribes from resource changes on the server.
|
||||||
|
|
@ -410,9 +391,7 @@ public open class Client(
|
||||||
public suspend fun unsubscribeResource(
|
public suspend fun unsubscribeResource(
|
||||||
request: UnsubscribeRequest,
|
request: UnsubscribeRequest,
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): EmptyRequestResult {
|
): EmptyRequestResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls a tool on the server by name, passing the specified arguments.
|
* Calls a tool on the server by name, passing the specified arguments.
|
||||||
|
|
@ -443,7 +422,7 @@ public open class Client(
|
||||||
|
|
||||||
val request = CallToolRequest(
|
val request = CallToolRequest(
|
||||||
name = name,
|
name = name,
|
||||||
arguments = JsonObject(jsonArguments)
|
arguments = JsonObject(jsonArguments),
|
||||||
)
|
)
|
||||||
return callTool(request, compatibility, options)
|
return callTool(request, compatibility, options)
|
||||||
}
|
}
|
||||||
|
|
@ -461,12 +440,10 @@ public open class Client(
|
||||||
request: CallToolRequest,
|
request: CallToolRequest,
|
||||||
compatibility: Boolean = false,
|
compatibility: Boolean = false,
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): CallToolResultBase? {
|
): CallToolResultBase? = if (compatibility) {
|
||||||
return if (compatibility) {
|
request<CompatibilityCallToolResult>(request, options)
|
||||||
request<CompatibilityCallToolResult>(request, options)
|
} else {
|
||||||
} else {
|
request<CallToolResult>(request, options)
|
||||||
request<CallToolResult>(request, options)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -480,9 +457,7 @@ public open class Client(
|
||||||
public suspend fun listTools(
|
public suspend fun listTools(
|
||||||
request: ListToolsRequest = ListToolsRequest(),
|
request: ListToolsRequest = ListToolsRequest(),
|
||||||
options: RequestOptions? = null,
|
options: RequestOptions? = null,
|
||||||
): ListToolsResult {
|
): ListToolsResult = request(request, options)
|
||||||
return request(request, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a single root.
|
* Registers a single root.
|
||||||
|
|
@ -491,10 +466,7 @@ public open class Client(
|
||||||
* @param name A human-readable name for the root.
|
* @param name A human-readable name for the root.
|
||||||
* @throws IllegalStateException If the client does not support roots.
|
* @throws IllegalStateException If the client does not support roots.
|
||||||
*/
|
*/
|
||||||
public fun addRoot(
|
public fun addRoot(uri: String, name: String) {
|
||||||
uri: String,
|
|
||||||
name: String,
|
|
||||||
) {
|
|
||||||
if (capabilities.roots == null) {
|
if (capabilities.roots == null) {
|
||||||
logger.error { "Failed to add root '$name': Client does not support roots capability" }
|
logger.error { "Failed to add root '$name': Client does not support roots capability" }
|
||||||
throw IllegalStateException("Client does not support roots capability.")
|
throw IllegalStateException("Client does not support roots capability.")
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import kotlin.time.Duration
|
||||||
*
|
*
|
||||||
* @param urlString Optional URL of the MCP server.
|
* @param urlString Optional URL of the MCP server.
|
||||||
* @param reconnectionTime Optional duration to wait before attempting to reconnect.
|
* @param reconnectionTime Optional duration to wait before attempting to reconnect.
|
||||||
* @param requestBuilder Optional lambda to configure the HTTP request.
|
* @param requestBuilder Optional lambda to configure the HTTP request.
|
||||||
* @return A [SSEClientTransport] configured for MCP communication.
|
* @return A [SSEClientTransport] configured for MCP communication.
|
||||||
*/
|
*/
|
||||||
public fun HttpClient.mcpSseTransport(
|
public fun HttpClient.mcpSseTransport(
|
||||||
|
|
@ -38,8 +38,8 @@ public suspend fun HttpClient.mcpSse(
|
||||||
val client = Client(
|
val client = Client(
|
||||||
Implementation(
|
Implementation(
|
||||||
name = IMPLEMENTATION_NAME,
|
name = IMPLEMENTATION_NAME,
|
||||||
version = LIB_VERSION
|
version = LIB_VERSION,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
client.connect(transport)
|
client.connect(transport)
|
||||||
return client
|
return client
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ public class SseClientTransport(
|
||||||
}
|
}
|
||||||
|
|
||||||
"endpoint" -> handleEndpoint(event.data.orEmpty())
|
"endpoint" -> handleEndpoint(event.data.orEmpty())
|
||||||
|
|
||||||
else -> handleMessage(event.data.orEmpty())
|
else -> handleMessage(event.data.orEmpty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,7 @@ import kotlin.coroutines.CoroutineContext
|
||||||
* @param output The output stream where messages are sent.
|
* @param output The output stream where messages are sent.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalAtomicApi::class)
|
@OptIn(ExperimentalAtomicApi::class)
|
||||||
public class StdioClientTransport(
|
public class StdioClientTransport(private val input: Source, private val output: Sink) : AbstractTransport() {
|
||||||
private val input: Source,
|
|
||||||
private val output: Sink
|
|
||||||
) : AbstractTransport() {
|
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
private val ioCoroutineContext: CoroutineContext = IODispatcher
|
private val ioCoroutineContext: CoroutineContext = IODispatcher
|
||||||
private val scope by lazy {
|
private val scope by lazy {
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,8 @@ private const val MCP_RESUMPTION_TOKEN_HEADER = "Last-Event-ID"
|
||||||
/**
|
/**
|
||||||
* Error class for Streamable HTTP transport errors.
|
* Error class for Streamable HTTP transport errors.
|
||||||
*/
|
*/
|
||||||
public class StreamableHttpError(
|
public class StreamableHttpError(public val code: Int? = null, message: String? = null) :
|
||||||
public val code: Int? = null,
|
Exception("Streamable HTTP error: $message")
|
||||||
message: String? = null
|
|
||||||
) : Exception("Streamable HTTP error: $message")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.
|
* Client transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.
|
||||||
|
|
@ -102,15 +100,16 @@ public class StreamableHttpClientTransport(
|
||||||
public suspend fun send(
|
public suspend fun send(
|
||||||
message: JSONRPCMessage,
|
message: JSONRPCMessage,
|
||||||
resumptionToken: String?,
|
resumptionToken: String?,
|
||||||
onResumptionToken: ((String) -> Unit)? = null
|
onResumptionToken: ((String) -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
logger.debug { "Client sending message via POST to $url: ${McpJson.encodeToString(message)}" }
|
logger.debug { "Client sending message via POST to $url: ${McpJson.encodeToString(message)}" }
|
||||||
|
|
||||||
// If we have a resumption token, reconnect the SSE stream with it
|
// If we have a resumption token, reconnect the SSE stream with it
|
||||||
resumptionToken?.let { token ->
|
resumptionToken?.let { token ->
|
||||||
startSseSession(
|
startSseSession(
|
||||||
resumptionToken = token, onResumptionToken = onResumptionToken,
|
resumptionToken = token,
|
||||||
replayMessageId = if (message is JSONRPCRequest) message.id else null
|
onResumptionToken = onResumptionToken,
|
||||||
|
replayMessageId = if (message is JSONRPCRequest) message.id else null,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -147,8 +146,9 @@ public class StreamableHttpClientTransport(
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentType.Text.EventStream -> handleInlineSse(
|
ContentType.Text.EventStream -> handleInlineSse(
|
||||||
response, onResumptionToken = onResumptionToken,
|
response,
|
||||||
replayMessageId = if (message is JSONRPCRequest) message.id else null
|
onResumptionToken = onResumptionToken,
|
||||||
|
replayMessageId = if (message is JSONRPCRequest) message.id else null,
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
|
@ -197,7 +197,7 @@ public class StreamableHttpClientTransport(
|
||||||
if (!response.status.isSuccess() && response.status != HttpStatusCode.MethodNotAllowed) {
|
if (!response.status.isSuccess() && response.status != HttpStatusCode.MethodNotAllowed) {
|
||||||
val error = StreamableHttpError(
|
val error = StreamableHttpError(
|
||||||
response.status.value,
|
response.status.value,
|
||||||
"Failed to terminate session: ${response.status.description}"
|
"Failed to terminate session: ${response.status.description}",
|
||||||
)
|
)
|
||||||
logger.error(error) { "Failed to terminate session" }
|
logger.error(error) { "Failed to terminate session" }
|
||||||
_onError(error)
|
_onError(error)
|
||||||
|
|
@ -212,7 +212,7 @@ public class StreamableHttpClientTransport(
|
||||||
private suspend fun startSseSession(
|
private suspend fun startSseSession(
|
||||||
resumptionToken: String? = null,
|
resumptionToken: String? = null,
|
||||||
replayMessageId: RequestId? = null,
|
replayMessageId: RequestId? = null,
|
||||||
onResumptionToken: ((String) -> Unit)? = null
|
onResumptionToken: ((String) -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
sseSession?.cancel()
|
sseSession?.cancel()
|
||||||
sseJob?.cancelAndJoin()
|
sseJob?.cancelAndJoin()
|
||||||
|
|
@ -254,7 +254,7 @@ public class StreamableHttpClientTransport(
|
||||||
private suspend fun collectSse(
|
private suspend fun collectSse(
|
||||||
session: ClientSSESession,
|
session: ClientSSESession,
|
||||||
replayMessageId: RequestId?,
|
replayMessageId: RequestId?,
|
||||||
onResumptionToken: ((String) -> Unit)?
|
onResumptionToken: ((String) -> Unit)?,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
session.incoming.collect { event ->
|
session.incoming.collect { event ->
|
||||||
|
|
@ -290,7 +290,7 @@ public class StreamableHttpClientTransport(
|
||||||
private suspend fun handleInlineSse(
|
private suspend fun handleInlineSse(
|
||||||
response: HttpResponse,
|
response: HttpResponse,
|
||||||
replayMessageId: RequestId?,
|
replayMessageId: RequestId?,
|
||||||
onResumptionToken: ((String) -> Unit)?
|
onResumptionToken: ((String) -> Unit)?,
|
||||||
) {
|
) {
|
||||||
logger.trace { "Handling inline SSE from POST response" }
|
logger.trace { "Handling inline SSE from POST response" }
|
||||||
val channel = response.bodyAsChannel()
|
val channel = response.bodyAsChannel()
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,4 @@ public suspend fun HttpClient.mcpStreamableHttp(
|
||||||
val client = Client(Implementation(name = IMPLEMENTATION_NAME, version = LIB_VERSION))
|
val client = Client(Implementation(name = IMPLEMENTATION_NAME, version = LIB_VERSION))
|
||||||
client.connect(transport)
|
client.connect(transport)
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ public suspend fun HttpClient.mcpWebSocket(
|
||||||
val client = Client(
|
val client = Client(
|
||||||
Implementation(
|
Implementation(
|
||||||
name = IMPLEMENTATION_NAME,
|
name = IMPLEMENTATION_NAME,
|
||||||
version = LIB_VERSION
|
version = LIB_VERSION,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
client.connect(transport)
|
client.connect(transport)
|
||||||
return client
|
return client
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class StreamableHttpClientTransportTest {
|
||||||
val message = JSONRPCRequest(
|
val message = JSONRPCRequest(
|
||||||
id = RequestId.StringId("test-id"),
|
id = RequestId.StringId("test-id"),
|
||||||
method = "test",
|
method = "test",
|
||||||
params = buildJsonObject { }
|
params = buildJsonObject { },
|
||||||
)
|
)
|
||||||
|
|
||||||
val transport = createTransport { request ->
|
val transport = createTransport { request ->
|
||||||
|
|
@ -61,7 +61,7 @@ class StreamableHttpClientTransportTest {
|
||||||
|
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.Accepted
|
status = HttpStatusCode.Accepted,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,26 +76,30 @@ class StreamableHttpClientTransportTest {
|
||||||
id = RequestId.StringId("test-id"),
|
id = RequestId.StringId("test-id"),
|
||||||
method = "initialize",
|
method = "initialize",
|
||||||
params = buildJsonObject {
|
params = buildJsonObject {
|
||||||
put("clientInfo", buildJsonObject {
|
put(
|
||||||
put("name", JsonPrimitive("test-client"))
|
"clientInfo",
|
||||||
put("version", JsonPrimitive("1.0"))
|
buildJsonObject {
|
||||||
})
|
put("name", JsonPrimitive("test-client"))
|
||||||
|
put("version", JsonPrimitive("1.0"))
|
||||||
|
},
|
||||||
|
)
|
||||||
put("protocolVersion", JsonPrimitive("2025-06-18"))
|
put("protocolVersion", JsonPrimitive("2025-06-18"))
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
val transport = createTransport { request ->
|
val transport = createTransport { request ->
|
||||||
when (val msg = McpJson.decodeFromString<JSONRPCMessage>((request.body as TextContent).text)) {
|
when (val msg = McpJson.decodeFromString<JSONRPCMessage>((request.body as TextContent).text)) {
|
||||||
is JSONRPCRequest if msg.method == "initialize" -> respond(
|
is JSONRPCRequest if msg.method == "initialize" -> respond(
|
||||||
content = "", status = HttpStatusCode.OK,
|
content = "",
|
||||||
headers = headersOf("mcp-session-id", "test-session-id")
|
status = HttpStatusCode.OK,
|
||||||
|
headers = headersOf("mcp-session-id", "test-session-id"),
|
||||||
)
|
)
|
||||||
|
|
||||||
is JSONRPCNotification if msg.method == "test" -> {
|
is JSONRPCNotification if msg.method == "test" -> {
|
||||||
assertEquals("test-session-id", request.headers["mcp-session-id"])
|
assertEquals("test-session-id", request.headers["mcp-session-id"])
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.Accepted
|
status = HttpStatusCode.Accepted,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +126,7 @@ class StreamableHttpClientTransportTest {
|
||||||
assertEquals("test-session-id", request.headers["mcp-session-id"])
|
assertEquals("test-session-id", request.headers["mcp-session-id"])
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.OK
|
status = HttpStatusCode.OK,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +145,7 @@ class StreamableHttpClientTransportTest {
|
||||||
assertEquals(HttpMethod.Delete, request.method)
|
assertEquals(HttpMethod.Delete, request.method)
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.MethodNotAllowed
|
status = HttpStatusCode.MethodNotAllowed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,7 +164,7 @@ class StreamableHttpClientTransportTest {
|
||||||
assertEquals("2025-06-18", request.headers["mcp-protocol-version"])
|
assertEquals("2025-06-18", request.headers["mcp-protocol-version"])
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.Accepted
|
status = HttpStatusCode.Accepted,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
transport.protocolVersion = "2025-06-18"
|
transport.protocolVersion = "2025-06-18"
|
||||||
|
|
@ -170,7 +174,8 @@ class StreamableHttpClientTransportTest {
|
||||||
transport.close()
|
transport.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore //Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support
|
// Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support
|
||||||
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
fun testNotificationSchemaE2E() = runTest {
|
fun testNotificationSchemaE2E() = runTest {
|
||||||
val receivedMessages = mutableListOf<JSONRPCMessage>()
|
val receivedMessages = mutableListOf<JSONRPCMessage>()
|
||||||
|
|
@ -182,7 +187,7 @@ class StreamableHttpClientTransportTest {
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.Accepted,
|
status = HttpStatusCode.Accepted,
|
||||||
headers = headersOf("mcp-session-id", "notification-test-session")
|
headers = headersOf("mcp-session-id", "notification-test-session"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +198,9 @@ class StreamableHttpClientTransportTest {
|
||||||
// Server sends various notifications
|
// Server sends various notifications
|
||||||
appendLine("event: message")
|
appendLine("event: message")
|
||||||
appendLine("id: 1")
|
appendLine("id: 1")
|
||||||
appendLine("""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}""")
|
appendLine(
|
||||||
|
"""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}""",
|
||||||
|
)
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
appendLine("event: message")
|
appendLine("event: message")
|
||||||
|
|
@ -210,8 +217,9 @@ class StreamableHttpClientTransportTest {
|
||||||
content = ByteReadChannel(sseContent),
|
content = ByteReadChannel(sseContent),
|
||||||
status = HttpStatusCode.OK,
|
status = HttpStatusCode.OK,
|
||||||
headers = headersOf(
|
headers = headersOf(
|
||||||
HttpHeaders.ContentType, ContentType.Text.EventStream.toString()
|
HttpHeaders.ContentType,
|
||||||
)
|
ContentType.Text.EventStream.toString(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +227,7 @@ class StreamableHttpClientTransportTest {
|
||||||
HttpMethod.Post -> {
|
HttpMethod.Post -> {
|
||||||
respond(
|
respond(
|
||||||
content = "",
|
content = "",
|
||||||
status = HttpStatusCode.Accepted
|
status = HttpStatusCode.Accepted,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,11 +246,14 @@ class StreamableHttpClientTransportTest {
|
||||||
method = "notifications/initialized",
|
method = "notifications/initialized",
|
||||||
params = buildJsonObject {
|
params = buildJsonObject {
|
||||||
put("protocolVersion", JsonPrimitive("1.0"))
|
put("protocolVersion", JsonPrimitive("1.0"))
|
||||||
put("capabilities", buildJsonObject {
|
put(
|
||||||
put("tools", JsonPrimitive(true))
|
"capabilities",
|
||||||
put("resources", JsonPrimitive(true))
|
buildJsonObject {
|
||||||
})
|
put("tools", JsonPrimitive(true))
|
||||||
}
|
put("resources", JsonPrimitive(true))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
transport.send(initializedNotification)
|
transport.send(initializedNotification)
|
||||||
|
|
@ -274,25 +285,28 @@ class StreamableHttpClientTransportTest {
|
||||||
params = buildJsonObject {
|
params = buildJsonObject {
|
||||||
put("progressToken", JsonPrimitive("download-456"))
|
put("progressToken", JsonPrimitive("download-456"))
|
||||||
put("progress", JsonPrimitive(75))
|
put("progress", JsonPrimitive(75))
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
JSONRPCNotification(
|
JSONRPCNotification(
|
||||||
method = "notifications/cancelled",
|
method = "notifications/cancelled",
|
||||||
params = buildJsonObject {
|
params = buildJsonObject {
|
||||||
put("requestId", JsonPrimitive("req-789"))
|
put("requestId", JsonPrimitive("req-789"))
|
||||||
put("reason", JsonPrimitive("user_cancelled"))
|
put("reason", JsonPrimitive("user_cancelled"))
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
JSONRPCNotification(
|
JSONRPCNotification(
|
||||||
method = "notifications/message",
|
method = "notifications/message",
|
||||||
params = buildJsonObject {
|
params = buildJsonObject {
|
||||||
put("level", JsonPrimitive("info"))
|
put("level", JsonPrimitive("info"))
|
||||||
put("message", JsonPrimitive("Operation completed"))
|
put("message", JsonPrimitive("Operation completed"))
|
||||||
put("data", buildJsonObject {
|
put(
|
||||||
put("duration", JsonPrimitive(1234))
|
"data",
|
||||||
})
|
buildJsonObject {
|
||||||
}
|
put("duration", JsonPrimitive(1234))
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Send all client notifications
|
// Send all client notifications
|
||||||
|
|
@ -305,7 +319,8 @@ class StreamableHttpClientTransportTest {
|
||||||
transport.close()
|
transport.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore // Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support
|
// Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support
|
||||||
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
fun testNotificationWithResumptionToken() = runTest {
|
fun testNotificationWithResumptionToken() = runTest {
|
||||||
var resumptionTokenReceived: String? = null
|
var resumptionTokenReceived: String? = null
|
||||||
|
|
@ -320,15 +335,18 @@ class StreamableHttpClientTransportTest {
|
||||||
val sseContent = buildString {
|
val sseContent = buildString {
|
||||||
appendLine("event: message")
|
appendLine("event: message")
|
||||||
appendLine("id: resume-100")
|
appendLine("id: resume-100")
|
||||||
appendLine("""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"${lastEventIdSent}"}}""")
|
appendLine(
|
||||||
|
"""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"$lastEventIdSent"}}""",
|
||||||
|
)
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
respond(
|
respond(
|
||||||
content = ByteReadChannel(sseContent),
|
content = ByteReadChannel(sseContent),
|
||||||
status = HttpStatusCode.OK,
|
status = HttpStatusCode.OK,
|
||||||
headers = headersOf(
|
headers = headersOf(
|
||||||
HttpHeaders.ContentType, ContentType.Text.EventStream.toString()
|
HttpHeaders.ContentType,
|
||||||
)
|
ContentType.Text.EventStream.toString(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,12 +362,12 @@ class StreamableHttpClientTransportTest {
|
||||||
method = "notifications/test",
|
method = "notifications/test",
|
||||||
params = buildJsonObject {
|
params = buildJsonObject {
|
||||||
put("data", JsonPrimitive("test-data"))
|
put("data", JsonPrimitive("test-data"))
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
resumptionToken = "previous-token-99",
|
resumptionToken = "previous-token-99",
|
||||||
onResumptionToken = { token ->
|
onResumptionToken = { token ->
|
||||||
resumptionTokenReceived = token
|
resumptionTokenReceived = token
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wait for response
|
// Wait for response
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,15 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
iosArm64(); iosX64(); iosSimulatorArm64()
|
iosArm64()
|
||||||
watchosX64(); watchosArm64(); watchosSimulatorArm64()
|
iosX64()
|
||||||
tvosX64(); tvosArm64(); tvosSimulatorArm64()
|
iosSimulatorArm64()
|
||||||
|
watchosX64()
|
||||||
|
watchosArm64()
|
||||||
|
watchosSimulatorArm64()
|
||||||
|
tvosX64()
|
||||||
|
tvosArm64()
|
||||||
|
tvosSimulatorArm64()
|
||||||
js {
|
js {
|
||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
|
|
@ -42,4 +48,4 @@ kotlin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ public data class RequestOptions(
|
||||||
/**
|
/**
|
||||||
* Extra data given to request handlers.
|
* Extra data given to request handlers.
|
||||||
*/
|
*/
|
||||||
public class RequestHandlerExtra()
|
public class RequestHandlerExtra
|
||||||
|
|
||||||
internal val COMPLETED = CompletableDeferred(Unit).also { it.complete(Unit) }
|
internal val COMPLETED = CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||||
|
|
||||||
|
|
@ -117,15 +117,20 @@ internal val COMPLETED = CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||||
* Implements MCP protocol framing on top of a pluggable transport, including
|
* Implements MCP protocol framing on top of a pluggable transport, including
|
||||||
* features like request/response linking, notifications, and progress.
|
* features like request/response linking, notifications, and progress.
|
||||||
*/
|
*/
|
||||||
public abstract class Protocol(
|
public abstract class Protocol(@PublishedApi internal val options: ProtocolOptions?) {
|
||||||
@PublishedApi internal val options: ProtocolOptions?,
|
|
||||||
) {
|
|
||||||
public var transport: Transport? = null
|
public var transport: Transport? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private val _requestHandlers: AtomicRef<PersistentMap<String, suspend (JSONRPCRequest, RequestHandlerExtra) -> RequestResult?>> =
|
private val _requestHandlers:
|
||||||
|
AtomicRef<PersistentMap<String, suspend (JSONRPCRequest, RequestHandlerExtra) -> RequestResult?>> =
|
||||||
atomic(persistentMapOf())
|
atomic(persistentMapOf())
|
||||||
public val requestHandlers: Map<String, suspend (request: JSONRPCRequest, extra: RequestHandlerExtra) -> RequestResult?>
|
public val requestHandlers: Map<
|
||||||
|
String,
|
||||||
|
suspend (
|
||||||
|
request: JSONRPCRequest,
|
||||||
|
extra: RequestHandlerExtra,
|
||||||
|
) -> RequestResult?,
|
||||||
|
>
|
||||||
get() = _requestHandlers.value
|
get() = _requestHandlers.value
|
||||||
|
|
||||||
private val _notificationHandlers =
|
private val _notificationHandlers =
|
||||||
|
|
@ -133,7 +138,8 @@ public abstract class Protocol(
|
||||||
public val notificationHandlers: Map<String, suspend (notification: JSONRPCNotification) -> Unit>
|
public val notificationHandlers: Map<String, suspend (notification: JSONRPCNotification) -> Unit>
|
||||||
get() = _notificationHandlers.value
|
get() = _notificationHandlers.value
|
||||||
|
|
||||||
private val _responseHandlers: AtomicRef<PersistentMap<RequestId, (response: JSONRPCResponse?, error: Exception?) -> Unit>> =
|
private val _responseHandlers:
|
||||||
|
AtomicRef<PersistentMap<RequestId, (response: JSONRPCResponse?, error: Exception?) -> Unit>> =
|
||||||
atomic(persistentMapOf())
|
atomic(persistentMapOf())
|
||||||
public val responseHandlers: Map<RequestId, (response: JSONRPCResponse?, error: Exception?) -> Unit>
|
public val responseHandlers: Map<RequestId, (response: JSONRPCResponse?, error: Exception?) -> Unit>
|
||||||
get() = _responseHandlers.value
|
get() = _responseHandlers.value
|
||||||
|
|
@ -161,7 +167,9 @@ public abstract class Protocol(
|
||||||
/**
|
/**
|
||||||
* A handler to invoke for any request types that do not have their own handler installed.
|
* A handler to invoke for any request types that do not have their own handler installed.
|
||||||
*/
|
*/
|
||||||
public var fallbackRequestHandler: (suspend (request: JSONRPCRequest, extra: RequestHandlerExtra) -> RequestResult?)? =
|
public var fallbackRequestHandler: (
|
||||||
|
suspend (request: JSONRPCRequest, extra: RequestHandlerExtra) -> RequestResult?
|
||||||
|
)? =
|
||||||
null
|
null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -251,8 +259,8 @@ public abstract class Protocol(
|
||||||
error = JSONRPCError(
|
error = JSONRPCError(
|
||||||
ErrorCode.Defined.MethodNotFound,
|
ErrorCode.Defined.MethodNotFound,
|
||||||
message = "Server does not support ${request.method}",
|
message = "Server does not support ${request.method}",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
} catch (cause: Throwable) {
|
} catch (cause: Throwable) {
|
||||||
LOGGER.error(cause) { "Error sending method not found response" }
|
LOGGER.error(cause) { "Error sending method not found response" }
|
||||||
|
|
@ -268,10 +276,9 @@ public abstract class Protocol(
|
||||||
transport?.send(
|
transport?.send(
|
||||||
JSONRPCResponse(
|
JSONRPCResponse(
|
||||||
id = request.id,
|
id = request.id,
|
||||||
result = result
|
result = result,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
} catch (cause: Throwable) {
|
} catch (cause: Throwable) {
|
||||||
LOGGER.error(cause) { "Error handling request: ${request.method} (id: ${request.id})" }
|
LOGGER.error(cause) { "Error handling request: ${request.method} (id: ${request.id})" }
|
||||||
|
|
||||||
|
|
@ -281,19 +288,23 @@ public abstract class Protocol(
|
||||||
id = request.id,
|
id = request.id,
|
||||||
error = JSONRPCError(
|
error = JSONRPCError(
|
||||||
code = ErrorCode.Defined.InternalError,
|
code = ErrorCode.Defined.InternalError,
|
||||||
message = cause.message ?: "Internal error"
|
message = cause.message ?: "Internal error",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
} catch (sendError: Throwable) {
|
} catch (sendError: Throwable) {
|
||||||
LOGGER.error(sendError) { "Failed to send error response for request: ${request.method} (id: ${request.id})" }
|
LOGGER.error(sendError) {
|
||||||
|
"Failed to send error response for request: ${request.method} (id: ${request.id})"
|
||||||
|
}
|
||||||
// Optionally implement fallback behavior here
|
// Optionally implement fallback behavior here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onProgress(notification: ProgressNotification) {
|
private fun onProgress(notification: ProgressNotification) {
|
||||||
LOGGER.trace { "Received progress notification: token=${notification.params.progressToken}, progress=${notification.params.progress}/${notification.params.total}" }
|
LOGGER.trace {
|
||||||
|
"Received progress notification: token=${notification.params.progressToken}, progress=${notification.params.progress}/${notification.params.total}"
|
||||||
|
}
|
||||||
val progress = notification.params.progress
|
val progress = notification.params.progress
|
||||||
val total = notification.params.total
|
val total = notification.params.total
|
||||||
val message = notification.params.message
|
val message = notification.params.message
|
||||||
|
|
@ -378,10 +389,7 @@ public abstract class Protocol(
|
||||||
*
|
*
|
||||||
* Do not use this method to emit notifications! Use notification() instead.
|
* Do not use this method to emit notifications! Use notification() instead.
|
||||||
*/
|
*/
|
||||||
public suspend fun <T : RequestResult> request(
|
public suspend fun <T : RequestResult> request(request: Request, options: RequestOptions? = null): T {
|
||||||
request: Request,
|
|
||||||
options: RequestOptions? = null,
|
|
||||||
): T {
|
|
||||||
LOGGER.trace { "Sending request: ${request.method}" }
|
LOGGER.trace { "Sending request: ${request.method}" }
|
||||||
val result = CompletableDeferred<T>()
|
val result = CompletableDeferred<T>()
|
||||||
val transport = this@Protocol.transport ?: throw Error("Not connected")
|
val transport = this@Protocol.transport ?: throw Error("Not connected")
|
||||||
|
|
@ -427,14 +435,14 @@ public abstract class Protocol(
|
||||||
|
|
||||||
val notification = CancelledNotification(
|
val notification = CancelledNotification(
|
||||||
params = CancelledNotification.Params(
|
params = CancelledNotification.Params(
|
||||||
requestId = messageId,
|
requestId = messageId,
|
||||||
reason = reason.message ?: "Unknown"
|
reason = reason.message ?: "Unknown",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
val serialized = JSONRPCNotification(
|
val serialized = JSONRPCNotification(
|
||||||
notification.method.value,
|
notification.method.value,
|
||||||
params = McpJson.encodeToJsonElement(notification)
|
params = McpJson.encodeToJsonElement(notification),
|
||||||
)
|
)
|
||||||
transport.send(serialized)
|
transport.send(serialized)
|
||||||
|
|
||||||
|
|
@ -454,7 +462,7 @@ public abstract class Protocol(
|
||||||
McpError(
|
McpError(
|
||||||
ErrorCode.Defined.RequestTimeout.code,
|
ErrorCode.Defined.RequestTimeout.code,
|
||||||
"Request timed out",
|
"Request timed out",
|
||||||
JsonObject(mutableMapOf("timeout" to JsonPrimitive(timeout.inWholeMilliseconds)))
|
JsonObject(mutableMapOf("timeout" to JsonPrimitive(timeout.inWholeMilliseconds))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
result.cancel(cause)
|
result.cancel(cause)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public class ReadBuffer {
|
||||||
var lfIndex = buffer.indexOf('\n'.code.toByte())
|
var lfIndex = buffer.indexOf('\n'.code.toByte())
|
||||||
val line = when (lfIndex) {
|
val line = when (lfIndex) {
|
||||||
-1L -> return null
|
-1L -> return null
|
||||||
|
|
||||||
0L -> {
|
0L -> {
|
||||||
buffer.skip(1)
|
buffer.skip(1)
|
||||||
return null
|
return null
|
||||||
|
|
@ -44,11 +45,6 @@ public class ReadBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun deserializeMessage(line: String): JSONRPCMessage {
|
internal fun deserializeMessage(line: String): JSONRPCMessage = McpJson.decodeFromString<JSONRPCMessage>(line)
|
||||||
return McpJson.decodeFromString<JSONRPCMessage>(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun serializeMessage(message: JSONRPCMessage): String {
|
|
||||||
return McpJson.encodeToString(message) + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public fun serializeMessage(message: JSONRPCMessage): String = McpJson.encodeToString(message) + "\n"
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public abstract class WebSocketMcpTransport : AbstractTransport() {
|
||||||
if (!initialized.compareAndSet(expectedValue = false, newValue = true)) {
|
if (!initialized.compareAndSet(expectedValue = false, newValue = true)) {
|
||||||
error(
|
error(
|
||||||
"WebSocketClientTransport already started! " +
|
"WebSocketClientTransport already started! " +
|
||||||
"If using Client class, note that connect() calls start() automatically.",
|
"If using Client class, note that connect() calls start() automatically.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,12 +168,10 @@ public sealed interface Notification {
|
||||||
*
|
*
|
||||||
* @return The JSON-RPC notification representation.
|
* @return The JSON-RPC notification representation.
|
||||||
*/
|
*/
|
||||||
public fun Notification.toJSON(): JSONRPCNotification {
|
public fun Notification.toJSON(): JSONRPCNotification = JSONRPCNotification(
|
||||||
return JSONRPCNotification(
|
method = method.value,
|
||||||
method = method.value,
|
params = McpJson.encodeToJsonElement(params),
|
||||||
params = McpJson.encodeToJsonElement(params),
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a JSON-RPC notification into a protocol-specific [Notification].
|
* Decodes a JSON-RPC notification into a protocol-specific [Notification].
|
||||||
|
|
@ -200,9 +198,9 @@ public sealed interface RequestResult : WithMeta
|
||||||
* @param _meta Additional metadata for the response. Defaults to an empty JSON object.
|
* @param _meta Additional metadata for the response. Defaults to an empty JSON object.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class EmptyRequestResult(
|
public data class EmptyRequestResult(override val _meta: JsonObject = EmptyJsonObject) :
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
ServerResult,
|
||||||
) : ServerResult, ClientResult
|
ClientResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A uniquely identifying ID for a request in JSON-RPC.
|
* A uniquely identifying ID for a request in JSON-RPC.
|
||||||
|
|
@ -282,7 +280,6 @@ public sealed interface ErrorCode {
|
||||||
MethodNotFound(-32601),
|
MethodNotFound(-32601),
|
||||||
InvalidParams(-32602),
|
InvalidParams(-32602),
|
||||||
InternalError(-32603),
|
InternalError(-32603),
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -293,11 +290,8 @@ public sealed interface ErrorCode {
|
||||||
* A response to a request that indicates an error occurred.
|
* A response to a request that indicates an error occurred.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class JSONRPCError(
|
public data class JSONRPCError(val code: ErrorCode, val message: String, val data: JsonObject = EmptyJsonObject) :
|
||||||
val code: ErrorCode,
|
JSONRPCMessage
|
||||||
val message: String,
|
|
||||||
val data: JsonObject = EmptyJsonObject,
|
|
||||||
) : JSONRPCMessage
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base interface for notification parameters with optional metadata.
|
* Base interface for notification parameters with optional metadata.
|
||||||
|
|
@ -316,9 +310,9 @@ public sealed interface NotificationParams : WithMeta
|
||||||
* A client MUST NOT attempt to cancel its `initialize` request.
|
* A client MUST NOT attempt to cancel its `initialize` request.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class CancelledNotification(
|
public data class CancelledNotification(override val params: Params) :
|
||||||
override val params: Params,
|
ClientNotification,
|
||||||
) : ClientNotification, ServerNotification {
|
ServerNotification {
|
||||||
override val method: Method = Method.Defined.NotificationsCancelled
|
override val method: Method = Method.Defined.NotificationsCancelled
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -342,10 +336,7 @@ public data class CancelledNotification(
|
||||||
* Describes the name and version of an MCP implementation.
|
* Describes the name and version of an MCP implementation.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Implementation(
|
public data class Implementation(val name: String, val version: String)
|
||||||
val name: String,
|
|
||||||
val version: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capabilities a client may support.
|
* Capabilities a client may support.
|
||||||
|
|
@ -383,7 +374,7 @@ public data class ClientCapabilities(
|
||||||
/**
|
/**
|
||||||
* Represents a request sent by the client.
|
* Represents a request sent by the client.
|
||||||
*/
|
*/
|
||||||
//@Serializable(with = ClientRequestPolymorphicSerializer::class)
|
// @Serializable(with = ClientRequestPolymorphicSerializer::class)
|
||||||
public interface ClientRequest : Request
|
public interface ClientRequest : Request
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -401,7 +392,7 @@ public sealed interface ClientResult : RequestResult
|
||||||
/**
|
/**
|
||||||
* Represents a request sent by the server.
|
* Represents a request sent by the server.
|
||||||
*/
|
*/
|
||||||
//@Serializable(with = ServerRequestPolymorphicSerializer::class)
|
// @Serializable(with = ServerRequestPolymorphicSerializer::class)
|
||||||
public sealed interface ServerRequest : Request
|
public sealed interface ServerRequest : Request
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -423,8 +414,12 @@ public sealed interface ServerResult : RequestResult
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class UnknownMethodRequestOrNotification(
|
public data class UnknownMethodRequestOrNotification(
|
||||||
override val method: Method, override val params: NotificationParams? = null,
|
override val method: Method,
|
||||||
) : ClientNotification, ClientRequest, ServerNotification, ServerRequest
|
override val params: NotificationParams? = null,
|
||||||
|
) : ClientNotification,
|
||||||
|
ClientRequest,
|
||||||
|
ServerNotification,
|
||||||
|
ServerRequest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This request is sent from the client to the server when it first connects, asking it to begin initialization.
|
* This request is sent from the client to the server when it first connects, asking it to begin initialization.
|
||||||
|
|
@ -435,7 +430,8 @@ public data class InitializeRequest(
|
||||||
val capabilities: ClientCapabilities,
|
val capabilities: ClientCapabilities,
|
||||||
val clientInfo: Implementation,
|
val clientInfo: Implementation,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.Initialize
|
override val method: Method = Method.Defined.Initialize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,15 +517,11 @@ public data class InitializeResult(
|
||||||
* This notification is sent from the client to the server after initialization has finished.
|
* This notification is sent from the client to the server after initialization has finished.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class InitializedNotification(
|
public data class InitializedNotification(override val params: Params = Params()) : ClientNotification {
|
||||||
override val params: Params = Params(),
|
|
||||||
) : ClientNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsInitialized
|
override val method: Method = Method.Defined.NotificationsInitialized
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Params(
|
public data class Params(override val _meta: JsonObject = EmptyJsonObject) : NotificationParams
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : NotificationParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ping */
|
/* Ping */
|
||||||
|
|
@ -538,7 +530,9 @@ public data class InitializedNotification(
|
||||||
* The receiver must promptly respond, or else it may be disconnected.
|
* The receiver must promptly respond, or else it may be disconnected.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public class PingRequest : ServerRequest, ClientRequest {
|
public class PingRequest :
|
||||||
|
ServerRequest,
|
||||||
|
ClientRequest {
|
||||||
override val method: Method = Method.Defined.Ping
|
override val method: Method = Method.Defined.Ping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -592,9 +586,9 @@ public open class Progress(
|
||||||
* An out-of-band notification used to inform the receiver of a progress update for a long-running request.
|
* An out-of-band notification used to inform the receiver of a progress update for a long-running request.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ProgressNotification(
|
public data class ProgressNotification(override val params: Params) :
|
||||||
override val params: Params,
|
ClientNotification,
|
||||||
) : ClientNotification, ServerNotification {
|
ServerNotification {
|
||||||
override val method: Method = Method.Defined.NotificationsProgress
|
override val method: Method = Method.Defined.NotificationsProgress
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -618,7 +612,8 @@ public data class ProgressNotification(
|
||||||
*/
|
*/
|
||||||
override val message: String? = null,
|
override val message: String? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : NotificationParams, ProgressBase
|
) : NotificationParams,
|
||||||
|
ProgressBase
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pagination */
|
/* Pagination */
|
||||||
|
|
@ -626,7 +621,9 @@ public data class ProgressNotification(
|
||||||
* Represents a request supporting pagination.
|
* Represents a request supporting pagination.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public sealed interface PaginatedRequest : Request, WithMeta {
|
public sealed interface PaginatedRequest :
|
||||||
|
Request,
|
||||||
|
WithMeta {
|
||||||
/**
|
/**
|
||||||
* The cursor indicating the pagination position.
|
* The cursor indicating the pagination position.
|
||||||
*/
|
*/
|
||||||
|
|
@ -669,11 +666,8 @@ public sealed interface ResourceContents {
|
||||||
* @property text The text of the item. This must only be set if the item can actually be represented as text (not binary data).
|
* @property text The text of the item. This must only be set if the item can actually be represented as text (not binary data).
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class TextResourceContents(
|
public data class TextResourceContents(val text: String, override val uri: String, override val mimeType: String?) :
|
||||||
val text: String,
|
ResourceContents
|
||||||
override val uri: String,
|
|
||||||
override val mimeType: String?,
|
|
||||||
) : ResourceContents
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the binary contents of a resource encoded as a base64 string.
|
* Represents the binary contents of a resource encoded as a base64 string.
|
||||||
|
|
@ -681,20 +675,14 @@ public data class TextResourceContents(
|
||||||
* @property blob A base64-encoded string representing the binary data of the item.
|
* @property blob A base64-encoded string representing the binary data of the item.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class BlobResourceContents(
|
public data class BlobResourceContents(val blob: String, override val uri: String, override val mimeType: String?) :
|
||||||
val blob: String,
|
ResourceContents
|
||||||
override val uri: String,
|
|
||||||
override val mimeType: String?,
|
|
||||||
) : ResourceContents
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents resource contents with unknown or unspecified data.
|
* Represents resource contents with unknown or unspecified data.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class UnknownResourceContents(
|
public data class UnknownResourceContents(override val uri: String, override val mimeType: String?) : ResourceContents
|
||||||
override val uri: String,
|
|
||||||
override val mimeType: String?,
|
|
||||||
) : ResourceContents
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A known resource that the server is capable of reading.
|
* A known resource that the server is capable of reading.
|
||||||
|
|
@ -758,8 +746,9 @@ public data class ResourceTemplate(
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ListResourcesRequest(
|
public data class ListResourcesRequest(
|
||||||
override val cursor: Cursor? = null,
|
override val cursor: Cursor? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, PaginatedRequest {
|
) : ClientRequest,
|
||||||
|
PaginatedRequest {
|
||||||
override val method: Method = Method.Defined.ResourcesList
|
override val method: Method = Method.Defined.ResourcesList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -771,7 +760,8 @@ public class ListResourcesResult(
|
||||||
public val resources: List<Resource>,
|
public val resources: List<Resource>,
|
||||||
override val nextCursor: Cursor? = null,
|
override val nextCursor: Cursor? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ServerResult, PaginatedResult
|
) : ServerResult,
|
||||||
|
PaginatedResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent from the client to request a list of resource templates the server has.
|
* Sent from the client to request a list of resource templates the server has.
|
||||||
|
|
@ -779,8 +769,9 @@ public class ListResourcesResult(
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ListResourceTemplatesRequest(
|
public data class ListResourceTemplatesRequest(
|
||||||
override val cursor: Cursor?,
|
override val cursor: Cursor?,
|
||||||
override val _meta: JsonObject = EmptyJsonObject
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, PaginatedRequest {
|
) : ClientRequest,
|
||||||
|
PaginatedRequest {
|
||||||
override val method: Method = Method.Defined.ResourcesTemplatesList
|
override val method: Method = Method.Defined.ResourcesTemplatesList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -792,16 +783,16 @@ public class ListResourceTemplatesResult(
|
||||||
public val resourceTemplates: List<ResourceTemplate>,
|
public val resourceTemplates: List<ResourceTemplate>,
|
||||||
override val nextCursor: Cursor? = null,
|
override val nextCursor: Cursor? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ServerResult, PaginatedResult
|
) : ServerResult,
|
||||||
|
PaginatedResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent from the client to the server to read a specific resource URI.
|
* Sent from the client to the server to read a specific resource URI.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ReadResourceRequest(
|
public data class ReadResourceRequest(val uri: String, override val _meta: JsonObject = EmptyJsonObject) :
|
||||||
val uri: String,
|
ClientRequest,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
WithMeta {
|
||||||
) : ClientRequest, WithMeta {
|
|
||||||
override val method: Method = Method.Defined.ResourcesRead
|
override val method: Method = Method.Defined.ResourcesRead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -820,15 +811,11 @@ public class ReadResourceResult(
|
||||||
* Servers may issue this without any previous subscription from the client.
|
* Servers may issue this without any previous subscription from the client.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ResourceListChangedNotification(
|
public data class ResourceListChangedNotification(override val params: Params = Params()) : ServerNotification {
|
||||||
override val params: Params = Params(),
|
|
||||||
) : ServerNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsResourcesListChanged
|
override val method: Method = Method.Defined.NotificationsResourcesListChanged
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Params(
|
public data class Params(override val _meta: JsonObject = EmptyJsonObject) : NotificationParams
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : NotificationParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -841,7 +828,8 @@ public data class SubscribeRequest(
|
||||||
*/
|
*/
|
||||||
val uri: String,
|
val uri: String,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.ResourcesSubscribe
|
override val method: Method = Method.Defined.ResourcesSubscribe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -855,7 +843,8 @@ public data class UnsubscribeRequest(
|
||||||
*/
|
*/
|
||||||
val uri: String,
|
val uri: String,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.ResourcesUnsubscribe
|
override val method: Method = Method.Defined.ResourcesUnsubscribe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -863,9 +852,7 @@ public data class UnsubscribeRequest(
|
||||||
* A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.
|
* A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ResourceUpdatedNotification(
|
public data class ResourceUpdatedNotification(override val params: Params) : ServerNotification {
|
||||||
override val params: Params,
|
|
||||||
) : ServerNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsResourcesUpdated
|
override val method: Method = Method.Defined.NotificationsResourcesUpdated
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -923,8 +910,9 @@ public class Prompt(
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ListPromptsRequest(
|
public data class ListPromptsRequest(
|
||||||
override val cursor: Cursor? = null,
|
override val cursor: Cursor? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, PaginatedRequest {
|
) : ClientRequest,
|
||||||
|
PaginatedRequest {
|
||||||
override val method: Method = Method.Defined.PromptsList
|
override val method: Method = Method.Defined.PromptsList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -936,7 +924,8 @@ public class ListPromptsResult(
|
||||||
public val prompts: List<Prompt>,
|
public val prompts: List<Prompt>,
|
||||||
override val nextCursor: Cursor? = null,
|
override val nextCursor: Cursor? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ServerResult, PaginatedResult
|
) : ServerResult,
|
||||||
|
PaginatedResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the client to get a prompt provided by the server.
|
* Used by the client to get a prompt provided by the server.
|
||||||
|
|
@ -954,7 +943,8 @@ public data class GetPromptRequest(
|
||||||
val arguments: Map<String, String>?,
|
val arguments: Map<String, String>?,
|
||||||
|
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.PromptsGet
|
override val method: Method = Method.Defined.PromptsGet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1033,22 +1023,17 @@ public data class AudioContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unknown content provided to or from an LLM.
|
* Unknown content provided to or from an LLM.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class UnknownContent(
|
public data class UnknownContent(override val type: String) : PromptMessageContentMultimodal
|
||||||
override val type: String,
|
|
||||||
) : PromptMessageContentMultimodal
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The contents of a resource, embedded into a prompt or tool call result.
|
* The contents of a resource, embedded into a prompt or tool call result.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class EmbeddedResource(
|
public data class EmbeddedResource(val resource: ResourceContents) : PromptMessageContent {
|
||||||
val resource: ResourceContents,
|
|
||||||
) : PromptMessageContent {
|
|
||||||
override val type: String = TYPE
|
override val type: String = TYPE
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
|
@ -1062,17 +1047,15 @@ public data class EmbeddedResource(
|
||||||
@Suppress("EnumEntryName")
|
@Suppress("EnumEntryName")
|
||||||
@Serializable
|
@Serializable
|
||||||
public enum class Role {
|
public enum class Role {
|
||||||
user, assistant,
|
user,
|
||||||
|
assistant,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a message returned as part of a prompt.
|
* Describes a message returned as part of a prompt.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class PromptMessage(
|
public data class PromptMessage(val role: Role, val content: PromptMessageContent)
|
||||||
val role: Role,
|
|
||||||
val content: PromptMessageContent,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The server's response to a prompts/get request from the client.
|
* The server's response to a prompts/get request from the client.
|
||||||
|
|
@ -1092,15 +1075,11 @@ public class GetPromptResult(
|
||||||
* Servers may issue this without any previous subscription from the client.
|
* Servers may issue this without any previous subscription from the client.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class PromptListChangedNotification(
|
public data class PromptListChangedNotification(override val params: Params = Params()) : ServerNotification {
|
||||||
override val params: Params = Params(),
|
|
||||||
) : ServerNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsPromptsListChanged
|
override val method: Method = Method.Defined.NotificationsPromptsListChanged
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Params(
|
public data class Params(override val _meta: JsonObject = EmptyJsonObject) : NotificationParams
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : NotificationParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tools */
|
/* Tools */
|
||||||
|
|
@ -1155,7 +1134,6 @@ public data class ToolAnnotations(
|
||||||
val openWorldHint: Boolean? = true,
|
val openWorldHint: Boolean? = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition for a tool the client can call.
|
* Definition for a tool the client can call.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1187,20 +1165,14 @@ public data class Tool(
|
||||||
val annotations: ToolAnnotations?,
|
val annotations: ToolAnnotations?,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Input(
|
public data class Input(val properties: JsonObject = EmptyJsonObject, val required: List<String>? = null) {
|
||||||
val properties: JsonObject = EmptyJsonObject,
|
|
||||||
val required: List<String>? = null,
|
|
||||||
) {
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
val type: String = "object"
|
val type: String = "object"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Output(
|
public data class Output(val properties: JsonObject = EmptyJsonObject, val required: List<String>? = null) {
|
||||||
val properties: JsonObject = EmptyJsonObject,
|
|
||||||
val required: List<String>? = null,
|
|
||||||
) {
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
@EncodeDefault
|
@EncodeDefault
|
||||||
val type: String = "object"
|
val type: String = "object"
|
||||||
|
|
@ -1213,8 +1185,9 @@ public data class Tool(
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ListToolsRequest(
|
public data class ListToolsRequest(
|
||||||
override val cursor: Cursor? = null,
|
override val cursor: Cursor? = null,
|
||||||
override val _meta: JsonObject = EmptyJsonObject
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, PaginatedRequest {
|
) : ClientRequest,
|
||||||
|
PaginatedRequest {
|
||||||
override val method: Method = Method.Defined.ToolsList
|
override val method: Method = Method.Defined.ToolsList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1226,7 +1199,8 @@ public class ListToolsResult(
|
||||||
public val tools: List<Tool>,
|
public val tools: List<Tool>,
|
||||||
override val nextCursor: Cursor?,
|
override val nextCursor: Cursor?,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ServerResult, PaginatedResult
|
) : ServerResult,
|
||||||
|
PaginatedResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The server's response to a tool call.
|
* The server's response to a tool call.
|
||||||
|
|
@ -1269,7 +1243,8 @@ public data class CallToolRequest(
|
||||||
val name: String,
|
val name: String,
|
||||||
val arguments: JsonObject = EmptyJsonObject,
|
val arguments: JsonObject = EmptyJsonObject,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.ToolsCall
|
override val method: Method = Method.Defined.ToolsCall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1278,15 +1253,11 @@ public data class CallToolRequest(
|
||||||
* Servers may issue this without any previous subscription from the client.
|
* Servers may issue this without any previous subscription from the client.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class ToolListChangedNotification(
|
public data class ToolListChangedNotification(override val params: Params = Params()) : ServerNotification {
|
||||||
override val params: Params = Params(),
|
|
||||||
) : ServerNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsToolsListChanged
|
override val method: Method = Method.Defined.NotificationsToolsListChanged
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Params(
|
public data class Params(override val _meta: JsonObject = EmptyJsonObject) : NotificationParams
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : NotificationParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Logging */
|
/* Logging */
|
||||||
|
|
@ -1304,7 +1275,6 @@ public enum class LoggingLevel {
|
||||||
critical,
|
critical,
|
||||||
alert,
|
alert,
|
||||||
emergency,
|
emergency,
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1313,9 +1283,7 @@ public enum class LoggingLevel {
|
||||||
* the server MAY decide which messages to send automatically.
|
* the server MAY decide which messages to send automatically.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class LoggingMessageNotification(
|
public data class LoggingMessageNotification(override val params: Params) : ServerNotification {
|
||||||
override val params: Params,
|
|
||||||
) : ServerNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsMessage
|
override val method: Method = Method.Defined.NotificationsMessage
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -1345,7 +1313,8 @@ public data class LoggingMessageNotification(
|
||||||
*/
|
*/
|
||||||
val level: LoggingLevel,
|
val level: LoggingLevel,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.LoggingSetLevel
|
override val method: Method = Method.Defined.LoggingSetLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1404,10 +1373,7 @@ public class ModelPreferences(
|
||||||
* Describes a message issued to or received from an LLM API.
|
* Describes a message issued to or received from an LLM API.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class SamplingMessage(
|
public data class SamplingMessage(val role: Role, val content: PromptMessageContentMultimodal)
|
||||||
val role: Role,
|
|
||||||
val content: PromptMessageContentMultimodal,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request from the server to sample an LLM via the client.
|
* A request from the server to sample an LLM via the client.
|
||||||
|
|
@ -1441,7 +1407,8 @@ public data class CreateMessageRequest(
|
||||||
*/
|
*/
|
||||||
val modelPreferences: ModelPreferences?,
|
val modelPreferences: ModelPreferences?,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ServerRequest, WithMeta {
|
) : ServerRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.SamplingCreateMessage
|
override val method: Method = Method.Defined.SamplingCreateMessage
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -1536,9 +1503,7 @@ public data class PromptReference(
|
||||||
* Identifies a prompt.
|
* Identifies a prompt.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class UnknownReference(
|
public data class UnknownReference(override val type: String) : Reference
|
||||||
override val type: String,
|
|
||||||
) : Reference
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request from the client to the server to ask for completion options.
|
* A request from the client to the server to ask for completion options.
|
||||||
|
|
@ -1551,7 +1516,8 @@ public data class CompleteRequest(
|
||||||
*/
|
*/
|
||||||
val argument: Argument,
|
val argument: Argument,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ClientRequest, WithMeta {
|
) : ClientRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.CompletionComplete
|
override val method: Method = Method.Defined.CompletionComplete
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -1571,10 +1537,8 @@ public data class CompleteRequest(
|
||||||
* The server's response to a completion/complete request
|
* The server's response to a completion/complete request
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class CompleteResult(
|
public data class CompleteResult(val completion: Completion, override val _meta: JsonObject = EmptyJsonObject) :
|
||||||
val completion: Completion,
|
ServerResult {
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : ServerResult {
|
|
||||||
@Suppress("CanBeParameter")
|
@Suppress("CanBeParameter")
|
||||||
@Serializable
|
@Serializable
|
||||||
public class Completion(
|
public class Completion(
|
||||||
|
|
@ -1626,7 +1590,9 @@ public data class Root(
|
||||||
* Sent from the server to request a list of root URIs from the client.
|
* Sent from the server to request a list of root URIs from the client.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public class ListRootsRequest(override val _meta: JsonObject = EmptyJsonObject) : ServerRequest, WithMeta {
|
public class ListRootsRequest(override val _meta: JsonObject = EmptyJsonObject) :
|
||||||
|
ServerRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.RootsList
|
override val method: Method = Method.Defined.RootsList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1634,24 +1600,18 @@ public class ListRootsRequest(override val _meta: JsonObject = EmptyJsonObject)
|
||||||
* The client's response to a roots/list request from the server.
|
* The client's response to a roots/list request from the server.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public class ListRootsResult(
|
public class ListRootsResult(public val roots: List<Root>, override val _meta: JsonObject = EmptyJsonObject) :
|
||||||
public val roots: List<Root>,
|
ClientResult
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : ClientResult
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A notification from the client to the server, informing it that the list of roots has changed.
|
* A notification from the client to the server, informing it that the list of roots has changed.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class RootsListChangedNotification(
|
public data class RootsListChangedNotification(override val params: Params = Params()) : ClientNotification {
|
||||||
override val params: Params = Params(),
|
|
||||||
) : ClientNotification {
|
|
||||||
override val method: Method = Method.Defined.NotificationsRootsListChanged
|
override val method: Method = Method.Defined.NotificationsRootsListChanged
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class Params(
|
public data class Params(override val _meta: JsonObject = EmptyJsonObject) : NotificationParams
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
|
||||||
) : NotificationParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1662,7 +1622,8 @@ public data class CreateElicitationRequest(
|
||||||
public val message: String,
|
public val message: String,
|
||||||
public val requestedSchema: RequestedSchema,
|
public val requestedSchema: RequestedSchema,
|
||||||
override val _meta: JsonObject = EmptyJsonObject,
|
override val _meta: JsonObject = EmptyJsonObject,
|
||||||
) : ServerRequest, WithMeta {
|
) : ServerRequest,
|
||||||
|
WithMeta {
|
||||||
override val method: Method = Method.Defined.ElicitationCreate
|
override val method: Method = Method.Defined.ElicitationCreate
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -1705,5 +1666,5 @@ public data class CreateElicitationResult(
|
||||||
*/
|
*/
|
||||||
public class McpError(public val code: Int, message: String, public val data: JsonObject = EmptyJsonObject) :
|
public class McpError(public val code: Int, message: String, public val data: JsonObject = EmptyJsonObject) :
|
||||||
Exception() {
|
Exception() {
|
||||||
override val message: String = "MCP error ${code}: $message"
|
override val message: String = "MCP error $code: $message"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,38 +73,35 @@ internal object StopReasonSerializer : KSerializer<StopReason> {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object ReferencePolymorphicSerializer : JsonContentPolymorphicSerializer<Reference>(Reference::class) {
|
internal object ReferencePolymorphicSerializer : JsonContentPolymorphicSerializer<Reference>(Reference::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<Reference> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<Reference> =
|
||||||
return when (element.jsonObject.getValue("type").jsonPrimitive.content) {
|
when (element.jsonObject.getValue("type").jsonPrimitive.content) {
|
||||||
ResourceReference.TYPE -> ResourceReference.serializer()
|
ResourceReference.TYPE -> ResourceReference.serializer()
|
||||||
PromptReference.TYPE -> PromptReference.serializer()
|
PromptReference.TYPE -> PromptReference.serializer()
|
||||||
else -> UnknownReference.serializer()
|
else -> UnknownReference.serializer()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object PromptMessageContentPolymorphicSerializer :
|
internal object PromptMessageContentPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<PromptMessageContent>(PromptMessageContent::class) {
|
JsonContentPolymorphicSerializer<PromptMessageContent>(PromptMessageContent::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<PromptMessageContent> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<PromptMessageContent> =
|
||||||
return when (element.jsonObject.getValue("type").jsonPrimitive.content) {
|
when (element.jsonObject.getValue("type").jsonPrimitive.content) {
|
||||||
ImageContent.TYPE -> ImageContent.serializer()
|
ImageContent.TYPE -> ImageContent.serializer()
|
||||||
TextContent.TYPE -> TextContent.serializer()
|
TextContent.TYPE -> TextContent.serializer()
|
||||||
EmbeddedResource.TYPE -> EmbeddedResource.serializer()
|
EmbeddedResource.TYPE -> EmbeddedResource.serializer()
|
||||||
AudioContent.TYPE -> AudioContent.serializer()
|
AudioContent.TYPE -> AudioContent.serializer()
|
||||||
else -> UnknownContent.serializer()
|
else -> UnknownContent.serializer()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object PromptMessageContentMultimodalPolymorphicSerializer :
|
internal object PromptMessageContentMultimodalPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<PromptMessageContentMultimodal>(PromptMessageContentMultimodal::class) {
|
JsonContentPolymorphicSerializer<PromptMessageContentMultimodal>(PromptMessageContentMultimodal::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<PromptMessageContentMultimodal> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<PromptMessageContentMultimodal> =
|
||||||
return when (element.jsonObject.getValue("type").jsonPrimitive.content) {
|
when (element.jsonObject.getValue("type").jsonPrimitive.content) {
|
||||||
ImageContent.TYPE -> ImageContent.serializer()
|
ImageContent.TYPE -> ImageContent.serializer()
|
||||||
TextContent.TYPE -> TextContent.serializer()
|
TextContent.TYPE -> TextContent.serializer()
|
||||||
AudioContent.TYPE -> AudioContent.serializer()
|
AudioContent.TYPE -> AudioContent.serializer()
|
||||||
else -> UnknownContent.serializer()
|
else -> UnknownContent.serializer()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object ResourceContentsPolymorphicSerializer :
|
internal object ResourceContentsPolymorphicSerializer :
|
||||||
|
|
@ -125,54 +122,48 @@ internal fun selectRequestDeserializer(method: String): DeserializationStrategy<
|
||||||
return CustomRequest.serializer()
|
return CustomRequest.serializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun selectClientRequestDeserializer(method: String): DeserializationStrategy<ClientRequest>? {
|
internal fun selectClientRequestDeserializer(method: String): DeserializationStrategy<ClientRequest>? = when (method) {
|
||||||
return when (method) {
|
Method.Defined.Ping.value -> PingRequest.serializer()
|
||||||
Method.Defined.Ping.value -> PingRequest.serializer()
|
Method.Defined.Initialize.value -> InitializeRequest.serializer()
|
||||||
Method.Defined.Initialize.value -> InitializeRequest.serializer()
|
Method.Defined.CompletionComplete.value -> CompleteRequest.serializer()
|
||||||
Method.Defined.CompletionComplete.value -> CompleteRequest.serializer()
|
Method.Defined.LoggingSetLevel.value -> SetLevelRequest.serializer()
|
||||||
Method.Defined.LoggingSetLevel.value -> SetLevelRequest.serializer()
|
Method.Defined.PromptsGet.value -> GetPromptRequest.serializer()
|
||||||
Method.Defined.PromptsGet.value -> GetPromptRequest.serializer()
|
Method.Defined.PromptsList.value -> ListPromptsRequest.serializer()
|
||||||
Method.Defined.PromptsList.value -> ListPromptsRequest.serializer()
|
Method.Defined.ResourcesList.value -> ListResourcesRequest.serializer()
|
||||||
Method.Defined.ResourcesList.value -> ListResourcesRequest.serializer()
|
Method.Defined.ResourcesTemplatesList.value -> ListResourceTemplatesRequest.serializer()
|
||||||
Method.Defined.ResourcesTemplatesList.value -> ListResourceTemplatesRequest.serializer()
|
Method.Defined.ResourcesRead.value -> ReadResourceRequest.serializer()
|
||||||
Method.Defined.ResourcesRead.value -> ReadResourceRequest.serializer()
|
Method.Defined.ResourcesSubscribe.value -> SubscribeRequest.serializer()
|
||||||
Method.Defined.ResourcesSubscribe.value -> SubscribeRequest.serializer()
|
Method.Defined.ResourcesUnsubscribe.value -> UnsubscribeRequest.serializer()
|
||||||
Method.Defined.ResourcesUnsubscribe.value -> UnsubscribeRequest.serializer()
|
Method.Defined.ToolsCall.value -> CallToolRequest.serializer()
|
||||||
Method.Defined.ToolsCall.value -> CallToolRequest.serializer()
|
Method.Defined.ToolsList.value -> ListToolsRequest.serializer()
|
||||||
Method.Defined.ToolsList.value -> ListToolsRequest.serializer()
|
else -> null
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectClientNotificationDeserializer(element: JsonElement): DeserializationStrategy<ClientNotification>? {
|
private fun selectClientNotificationDeserializer(element: JsonElement): DeserializationStrategy<ClientNotification>? =
|
||||||
return when (element.jsonObject.getValue("method").jsonPrimitive.content) {
|
when (element.jsonObject.getValue("method").jsonPrimitive.content) {
|
||||||
Method.Defined.NotificationsCancelled.value -> CancelledNotification.serializer()
|
Method.Defined.NotificationsCancelled.value -> CancelledNotification.serializer()
|
||||||
Method.Defined.NotificationsProgress.value -> ProgressNotification.serializer()
|
Method.Defined.NotificationsProgress.value -> ProgressNotification.serializer()
|
||||||
Method.Defined.NotificationsInitialized.value -> InitializedNotification.serializer()
|
Method.Defined.NotificationsInitialized.value -> InitializedNotification.serializer()
|
||||||
Method.Defined.NotificationsRootsListChanged.value -> RootsListChangedNotification.serializer()
|
Method.Defined.NotificationsRootsListChanged.value -> RootsListChangedNotification.serializer()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal object ClientNotificationPolymorphicSerializer :
|
internal object ClientNotificationPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<ClientNotification>(ClientNotification::class) {
|
JsonContentPolymorphicSerializer<ClientNotification>(ClientNotification::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ClientNotification> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ClientNotification> =
|
||||||
return selectClientNotificationDeserializer(element)
|
selectClientNotificationDeserializer(element)
|
||||||
?: UnknownMethodRequestOrNotification.serializer()
|
?: UnknownMethodRequestOrNotification.serializer()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun selectServerRequestDeserializer(method: String): DeserializationStrategy<ServerRequest>? {
|
internal fun selectServerRequestDeserializer(method: String): DeserializationStrategy<ServerRequest>? = when (method) {
|
||||||
return when (method) {
|
Method.Defined.Ping.value -> PingRequest.serializer()
|
||||||
Method.Defined.Ping.value -> PingRequest.serializer()
|
Method.Defined.SamplingCreateMessage.value -> CreateMessageRequest.serializer()
|
||||||
Method.Defined.SamplingCreateMessage.value -> CreateMessageRequest.serializer()
|
Method.Defined.RootsList.value -> ListRootsRequest.serializer()
|
||||||
Method.Defined.RootsList.value -> ListRootsRequest.serializer()
|
else -> null
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun selectServerNotificationDeserializer(element: JsonElement): DeserializationStrategy<ServerNotification>? {
|
internal fun selectServerNotificationDeserializer(element: JsonElement): DeserializationStrategy<ServerNotification>? =
|
||||||
return when (element.jsonObject.getValue("method").jsonPrimitive.content) {
|
when (element.jsonObject.getValue("method").jsonPrimitive.content) {
|
||||||
Method.Defined.NotificationsCancelled.value -> CancelledNotification.serializer()
|
Method.Defined.NotificationsCancelled.value -> CancelledNotification.serializer()
|
||||||
Method.Defined.NotificationsProgress.value -> ProgressNotification.serializer()
|
Method.Defined.NotificationsProgress.value -> ProgressNotification.serializer()
|
||||||
Method.Defined.NotificationsMessage.value -> LoggingMessageNotification.serializer()
|
Method.Defined.NotificationsMessage.value -> LoggingMessageNotification.serializer()
|
||||||
|
|
@ -182,23 +173,20 @@ internal fun selectServerNotificationDeserializer(element: JsonElement): Deseria
|
||||||
Method.Defined.NotificationsPromptsListChanged.value -> PromptListChangedNotification.serializer()
|
Method.Defined.NotificationsPromptsListChanged.value -> PromptListChangedNotification.serializer()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal object ServerNotificationPolymorphicSerializer :
|
internal object ServerNotificationPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<ServerNotification>(ServerNotification::class) {
|
JsonContentPolymorphicSerializer<ServerNotification>(ServerNotification::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ServerNotification> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ServerNotification> =
|
||||||
return selectServerNotificationDeserializer(element)
|
selectServerNotificationDeserializer(element)
|
||||||
?: UnknownMethodRequestOrNotification.serializer()
|
?: UnknownMethodRequestOrNotification.serializer()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object NotificationPolymorphicSerializer :
|
internal object NotificationPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<Notification>(Notification::class) {
|
JsonContentPolymorphicSerializer<Notification>(Notification::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<Notification> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<Notification> =
|
||||||
return selectClientNotificationDeserializer(element)
|
selectClientNotificationDeserializer(element)
|
||||||
?: selectServerNotificationDeserializer(element)
|
?: selectServerNotificationDeserializer(element)
|
||||||
?: UnknownMethodRequestOrNotification.serializer()
|
?: UnknownMethodRequestOrNotification.serializer()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object RequestPolymorphicSerializer :
|
internal object RequestPolymorphicSerializer :
|
||||||
|
|
@ -243,27 +231,24 @@ private fun selectClientResultDeserializer(element: JsonElement): Deserializatio
|
||||||
|
|
||||||
internal object ServerResultPolymorphicSerializer :
|
internal object ServerResultPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<ServerResult>(ServerResult::class) {
|
JsonContentPolymorphicSerializer<ServerResult>(ServerResult::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ServerResult> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ServerResult> =
|
||||||
return selectServerResultDeserializer(element)
|
selectServerResultDeserializer(element)
|
||||||
?: EmptyRequestResult.serializer()
|
?: EmptyRequestResult.serializer()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object ClientResultPolymorphicSerializer :
|
internal object ClientResultPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<ClientResult>(ClientResult::class) {
|
JsonContentPolymorphicSerializer<ClientResult>(ClientResult::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ClientResult> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ClientResult> =
|
||||||
return selectClientResultDeserializer(element)
|
selectClientResultDeserializer(element)
|
||||||
?: EmptyRequestResult.serializer()
|
?: EmptyRequestResult.serializer()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object RequestResultPolymorphicSerializer :
|
internal object RequestResultPolymorphicSerializer :
|
||||||
JsonContentPolymorphicSerializer<RequestResult>(RequestResult::class) {
|
JsonContentPolymorphicSerializer<RequestResult>(RequestResult::class) {
|
||||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RequestResult> {
|
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RequestResult> =
|
||||||
return selectClientResultDeserializer(element)
|
selectClientResultDeserializer(element)
|
||||||
?: selectServerResultDeserializer(element)
|
?: selectServerResultDeserializer(element)
|
||||||
?: EmptyRequestResult.serializer()
|
?: EmptyRequestResult.serializer()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object JSONRPCMessagePolymorphicSerializer :
|
internal object JSONRPCMessagePolymorphicSerializer :
|
||||||
|
|
@ -315,7 +300,7 @@ public fun CallToolResult.Companion.ok(content: String, meta: JsonObject = Empty
|
||||||
CallToolResult(
|
CallToolResult(
|
||||||
content = listOf(TextContent(content)),
|
content = listOf(TextContent(content)),
|
||||||
isError = false,
|
isError = false,
|
||||||
_meta = meta
|
_meta = meta,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -325,5 +310,5 @@ public fun CallToolResult.Companion.error(content: String, meta: JsonObject = Em
|
||||||
CallToolResult(
|
CallToolResult(
|
||||||
content = listOf(TextContent(content)),
|
content = listOf(TextContent(content)),
|
||||||
isError = true,
|
isError = true,
|
||||||
_meta = meta
|
_meta = meta,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class AudioContentSerializationTest {
|
||||||
|
|
||||||
private val audioContent = AudioContent(
|
private val audioContent = AudioContent(
|
||||||
data = "base64-encoded-audio-data",
|
data = "base64-encoded-audio-data",
|
||||||
mimeType = "audio/wav"
|
mimeType = "audio/wav",
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -30,4 +30,4 @@ class AudioContentSerializationTest {
|
||||||
val content = McpJson.decodeFromString<AudioContent>(audioContentJson)
|
val content = McpJson.decodeFromString<AudioContent>(audioContentJson)
|
||||||
assertEquals(expected = audioContent, actual = content)
|
assertEquals(expected = audioContent, actual = content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,4 +61,3 @@ class CallToolResultUtilsTest {
|
||||||
assertEquals(meta, result._meta)
|
assertEquals(meta, result._meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue