Enable Explicit Api (#21)
This commit is contained in:
parent
a63ee76ff6
commit
852a4a3d5e
21 changed files with 291 additions and 272 deletions
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
|
|
@ -2,6 +2,5 @@
|
|||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/samples/kotlin-mcp-server" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import org.jetbrains.dokka.gradle.engine.parameters.VisibilityModifier
|
||||
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
|
|
@ -207,6 +208,8 @@ val generateLibVersionTask =
|
|||
tasks.register<GenerateLibVersionTask>("generateLibVersion", version.toString(), sourcesDir)
|
||||
|
||||
kotlin {
|
||||
explicitApi = ExplicitApiMode.Strict
|
||||
|
||||
jvmToolchain(21)
|
||||
|
||||
sourceSets {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ import org.jetbrains.kotlinx.mcp.shared.Transport
|
|||
* @property capabilities The capabilities this client supports.
|
||||
* @property enforceStrictCapabilities Whether to strictly enforce capabilities when interacting with the server.
|
||||
*/
|
||||
class ClientOptions(
|
||||
val capabilities: ClientCapabilities = ClientCapabilities(),
|
||||
public class ClientOptions(
|
||||
public val capabilities: ClientCapabilities = ClientCapabilities(),
|
||||
enforceStrictCapabilities: Boolean = true,
|
||||
) : ProtocolOptions(enforceStrictCapabilities = enforceStrictCapabilities)
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ class ClientOptions(
|
|||
* @param clientInfo Information about the client implementation (name, version).
|
||||
* @param options Configuration options for this client.
|
||||
*/
|
||||
open class Client(
|
||||
public open class Client(
|
||||
private val clientInfo: Implementation,
|
||||
options: ClientOptions = ClientOptions(),
|
||||
) : Protocol<ClientRequest, ClientNotification, ClientResult>(options) {
|
||||
|
|
@ -95,7 +95,7 @@ open class Client(
|
|||
*
|
||||
* @return The server's capabilities, or `null` if initialization is not yet complete.
|
||||
*/
|
||||
fun getServerCapabilities(): ServerCapabilities? {
|
||||
public fun getServerCapabilities(): ServerCapabilities? {
|
||||
return serverCapabilities
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ open class Client(
|
|||
*
|
||||
* @return Information about the server's implementation, or `null` if initialization is not yet complete.
|
||||
*/
|
||||
fun getServerVersion(): Implementation? {
|
||||
public fun getServerVersion(): Implementation? {
|
||||
return serverVersion
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ open class Client(
|
|||
* @param options Optional request options.
|
||||
* @throws IllegalStateException If the server does not support the ping method (unlikely).
|
||||
*/
|
||||
suspend fun ping(options: RequestOptions? = null): EmptyRequestResult {
|
||||
public suspend fun ping(options: RequestOptions? = null): EmptyRequestResult {
|
||||
return request<EmptyRequestResult>(PingRequest(), options)
|
||||
}
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ open class Client(
|
|||
* @return The completion result returned by the server, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support prompts or completion.
|
||||
*/
|
||||
suspend fun complete(params: CompleteRequest, options: RequestOptions? = null): CompleteResult? {
|
||||
public suspend fun complete(params: CompleteRequest, options: RequestOptions? = null): CompleteResult? {
|
||||
return request<CompleteResult>(params, options)
|
||||
}
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ open class Client(
|
|||
* @param options Optional request options.
|
||||
* @throws IllegalStateException If the server does not support logging.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ open class Client(
|
|||
* @return The requested prompt details, or `null` if not found.
|
||||
* @throws IllegalStateException If the server does not support prompts.
|
||||
*/
|
||||
suspend fun getPrompt(request: GetPromptRequest, options: RequestOptions? = null): GetPromptResult? {
|
||||
public suspend fun getPrompt(request: GetPromptRequest, options: RequestOptions? = null): GetPromptResult? {
|
||||
return request<GetPromptResult>(request, options)
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ open class Client(
|
|||
* @return The list of available prompts, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support prompts.
|
||||
*/
|
||||
suspend fun listPrompts(
|
||||
public suspend fun listPrompts(
|
||||
request: ListPromptsRequest = ListPromptsRequest(),
|
||||
options: RequestOptions? = null,
|
||||
): ListPromptsResult? {
|
||||
|
|
@ -281,7 +281,7 @@ open class Client(
|
|||
* @return The list of resources, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support resources.
|
||||
*/
|
||||
suspend fun listResources(
|
||||
public suspend fun listResources(
|
||||
request: ListResourcesRequest = ListResourcesRequest(),
|
||||
options: RequestOptions? = null,
|
||||
): ListResourcesResult? {
|
||||
|
|
@ -296,7 +296,7 @@ open class Client(
|
|||
* @return The list of resource templates, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support resources.
|
||||
*/
|
||||
suspend fun listResourceTemplates(
|
||||
public suspend fun listResourceTemplates(
|
||||
request: ListResourceTemplatesRequest,
|
||||
options: RequestOptions? = null,
|
||||
): ListResourceTemplatesResult? {
|
||||
|
|
@ -311,7 +311,10 @@ open class Client(
|
|||
* @return The resource content, or `null` if the resource is not found.
|
||||
* @throws IllegalStateException If the server does not support resources.
|
||||
*/
|
||||
suspend fun readResource(request: ReadResourceRequest, options: RequestOptions? = null): ReadResourceResult? {
|
||||
public suspend fun readResource(
|
||||
request: ReadResourceRequest,
|
||||
options: RequestOptions? = null,
|
||||
): ReadResourceResult? {
|
||||
return request<ReadResourceResult>(request, options)
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +325,10 @@ open class Client(
|
|||
* @param options Optional request options.
|
||||
* @throws IllegalStateException If the server does not support resource subscriptions.
|
||||
*/
|
||||
suspend fun subscribeResource(request: SubscribeRequest, options: RequestOptions? = null): EmptyRequestResult {
|
||||
public suspend fun subscribeResource(
|
||||
request: SubscribeRequest,
|
||||
options: RequestOptions? = null,
|
||||
): EmptyRequestResult {
|
||||
return request<EmptyRequestResult>(request, options)
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +339,10 @@ open class Client(
|
|||
* @param options Optional request options.
|
||||
* @throws IllegalStateException If the server does not support resource subscriptions.
|
||||
*/
|
||||
suspend fun unsubscribeResource(request: UnsubscribeRequest, options: RequestOptions? = null): EmptyRequestResult {
|
||||
public suspend fun unsubscribeResource(
|
||||
request: UnsubscribeRequest,
|
||||
options: RequestOptions? = null,
|
||||
): EmptyRequestResult {
|
||||
return request<EmptyRequestResult>(request, options)
|
||||
}
|
||||
|
||||
|
|
@ -347,7 +356,7 @@ open class Client(
|
|||
* @return The result of the tool call, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support tools.
|
||||
*/
|
||||
suspend fun callTool(
|
||||
public suspend fun callTool(
|
||||
name: String,
|
||||
arguments: Map<String, Any?>,
|
||||
compatibility: Boolean = false,
|
||||
|
|
@ -380,7 +389,7 @@ open class Client(
|
|||
* @return The result of the tool call, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support tools.
|
||||
*/
|
||||
suspend fun callTool(
|
||||
public suspend fun callTool(
|
||||
request: CallToolRequest,
|
||||
compatibility: Boolean = false,
|
||||
options: RequestOptions? = null,
|
||||
|
|
@ -400,7 +409,7 @@ open class Client(
|
|||
* @return The list of available tools, or `null` if none.
|
||||
* @throws IllegalStateException If the server does not support tools.
|
||||
*/
|
||||
suspend fun listTools(
|
||||
public suspend fun listTools(
|
||||
request: ListToolsRequest = ListToolsRequest(),
|
||||
options: RequestOptions? = null,
|
||||
): ListToolsResult? {
|
||||
|
|
@ -413,7 +422,7 @@ open class Client(
|
|||
*
|
||||
* @throws IllegalStateException If the client or server does not support roots.
|
||||
*/
|
||||
suspend fun sendRootsListChanged() {
|
||||
public suspend fun sendRootsListChanged() {
|
||||
notification(RootsListChangedNotification())
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ import kotlin.time.Duration
|
|||
* Client transport for SSE: this will connect to a server using Server-Sent Events for receiving
|
||||
* messages and make separate POST requests for sending messages.
|
||||
*/
|
||||
class SSEClientTransport(
|
||||
public class SSEClientTransport(
|
||||
private val client: HttpClient,
|
||||
private val urlString: String?,
|
||||
private val reconnectionTime: Duration? = null,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import kotlin.text.Charsets.UTF_8
|
|||
* @param input The input stream where messages are received.
|
||||
* @param output The output stream where messages are sent.
|
||||
*/
|
||||
class StdioClientTransport(
|
||||
public class StdioClientTransport(
|
||||
private val input: InputStream,
|
||||
private val output: OutputStream
|
||||
) : Transport {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import kotlin.properties.Delegates
|
|||
/**
|
||||
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
|
||||
*/
|
||||
class WebSocketClientTransport(
|
||||
public class WebSocketClientTransport(
|
||||
private val client: HttpClient,
|
||||
private val urlString: String?,
|
||||
private val requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import org.jetbrains.kotlinx.mcp.shared.IMPLEMENTATION_NAME
|
|||
* @param requestBuilder Optional lambda to configure the HTTP request.
|
||||
* @return A [WebSocketClientTransport] configured for MCP communication.
|
||||
*/
|
||||
fun HttpClient.mcpWebSocketTransport(
|
||||
public fun HttpClient.mcpWebSocketTransport(
|
||||
urlString: String? = null,
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
): WebSocketClientTransport = WebSocketClientTransport(this, urlString, requestBuilder)
|
||||
|
|
@ -25,7 +25,7 @@ fun HttpClient.mcpWebSocketTransport(
|
|||
* @param requestBuilder Optional lambda to configure the HTTP request.
|
||||
* @return A connected [Client] ready for MCP communication.
|
||||
*/
|
||||
suspend fun HttpClient.mcpWebSocket(
|
||||
public suspend fun HttpClient.mcpWebSocket(
|
||||
urlString: String? = null,
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
): Client {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import kotlin.time.Duration
|
|||
* @param requestBuilder Optional lambda to configure the HTTP request.
|
||||
* @return A [SSEClientTransport] configured for MCP communication.
|
||||
*/
|
||||
fun HttpClient.mcpSseTransport(
|
||||
public fun HttpClient.mcpSseTransport(
|
||||
urlString: String? = null,
|
||||
reconnectionTime: Duration? = null,
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
|
|
@ -29,7 +29,7 @@ fun HttpClient.mcpSseTransport(
|
|||
* @param requestBuilder Optional lambda to configure the HTTP request.
|
||||
* @return A connected [Client] ready for MCP communication.
|
||||
*/
|
||||
suspend fun HttpClient.mcpSse(
|
||||
public suspend fun HttpClient.mcpSse(
|
||||
urlString: String? = null,
|
||||
reconnectionTime: Duration? = null,
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ private val logger = KotlinLogging.logger {}
|
|||
/**
|
||||
* Configures the Ktor Application to handle Model Context Protocol (MCP) over Server-Sent Events (SSE).
|
||||
*/
|
||||
fun Application.MCP(block: () -> Server) {
|
||||
public fun Application.MCP(block: () -> Server) {
|
||||
val servers = ConcurrentMap<String, Server>()
|
||||
|
||||
install(SSE)
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ internal const val SESSION_ID_PARAM = "sessionId"
|
|||
*
|
||||
* Creates a new SSE server transport, which will direct the client to POST messages to the relative or absolute URL identified by `_endpoint`.
|
||||
*/
|
||||
class SSEServerTransport(
|
||||
public class SSEServerTransport(
|
||||
private val endpoint: String,
|
||||
private val session: ServerSSESession,
|
||||
) : Transport {
|
||||
private val initialized = AtomicBoolean(false)
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
val sessionId: String = Uuid.random().toString()
|
||||
public val sessionId: String = Uuid.random().toString()
|
||||
|
||||
override var onClose: (() -> Unit)? = null
|
||||
override var onError: ((Throwable) -> Unit)? = null
|
||||
|
|
@ -62,7 +62,7 @@ class SSEServerTransport(
|
|||
*
|
||||
* This should be called when a POST request is made to send a message to the server.
|
||||
*/
|
||||
suspend fun handlePostMessage(call: ApplicationCall) {
|
||||
public suspend fun handlePostMessage(call: ApplicationCall) {
|
||||
if (!initialized.get()) {
|
||||
val message = "SSE connection not established"
|
||||
call.respondText(message, status = HttpStatusCode.InternalServerError)
|
||||
|
|
@ -96,7 +96,7 @@ class SSEServerTransport(
|
|||
* Handle a client message, regardless of how it arrived.
|
||||
* This can be used to inform the server of messages that arrive via a means different from HTTP POST.
|
||||
*/
|
||||
suspend fun handleMessage(message: String) {
|
||||
public suspend fun handleMessage(message: String) {
|
||||
try {
|
||||
val parsedMessage = McpJson.decodeFromString<JSONRPCMessage>(message)
|
||||
onMessage?.invoke(parsedMessage)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ private val logger = KotlinLogging.logger {}
|
|||
* @property capabilities The capabilities this server supports.
|
||||
* @property enforceStrictCapabilities Whether to strictly enforce capabilities when interacting with clients.
|
||||
*/
|
||||
class ServerOptions(
|
||||
val capabilities: ServerCapabilities,
|
||||
public class ServerOptions(
|
||||
public val capabilities: ServerCapabilities,
|
||||
enforceStrictCapabilities: Boolean = true,
|
||||
) : ProtocolOptions(enforceStrictCapabilities = enforceStrictCapabilities)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import kotlin.coroutines.CoroutineContext
|
|||
*
|
||||
* Reads from System.in and writes to System.out.
|
||||
*/
|
||||
class StdioServerTransport(
|
||||
public class StdioServerTransport(
|
||||
private val inputStream: BufferedInputStream = BufferedInputStream(System.`in`),
|
||||
private val outputStream: PrintStream = System.out
|
||||
outputStream: PrintStream = System.out
|
||||
) : Transport {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
override var onClose: (() -> Unit)? = null
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import org.jetbrains.kotlinx.mcp.shared.IMPLEMENTATION_NAME
|
|||
* @param options Optional server configuration settings for the MCP server.
|
||||
* @param handler A suspend function that defines the server's behavior.
|
||||
*/
|
||||
fun Route.mcpWebSocket(
|
||||
public fun Route.mcpWebSocket(
|
||||
options: ServerOptions? = null,
|
||||
handler: suspend Server.() -> Unit = {},
|
||||
) {
|
||||
|
|
@ -29,7 +29,7 @@ fun Route.mcpWebSocket(
|
|||
* @param options Optional server configuration settings for the MCP server.
|
||||
* @param handler A suspend function that defines the server's behavior.
|
||||
*/
|
||||
fun Route.mcpWebSocket(
|
||||
public fun Route.mcpWebSocket(
|
||||
path: String,
|
||||
options: ServerOptions? = null,
|
||||
handler: suspend Server.() -> Unit = {},
|
||||
|
|
@ -44,7 +44,7 @@ fun Route.mcpWebSocket(
|
|||
*
|
||||
* @param handler A suspend function that defines the behavior of the transport layer.
|
||||
*/
|
||||
fun Route.mcpWebSocketTransport(
|
||||
public fun Route.mcpWebSocketTransport(
|
||||
handler: suspend WebSocketMcpServerTransport.() -> Unit = {},
|
||||
) {
|
||||
webSocket {
|
||||
|
|
@ -61,7 +61,7 @@ fun Route.mcpWebSocketTransport(
|
|||
* @param path The URL path at which to register the WebSocket route.
|
||||
* @param handler A suspend function that defines the behavior of the transport layer.
|
||||
*/
|
||||
fun Route.mcpWebSocketTransport(
|
||||
public fun Route.mcpWebSocketTransport(
|
||||
path: String,
|
||||
handler: suspend WebSocketMcpServerTransport.() -> Unit = {},
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import org.jetbrains.kotlinx.mcp.shared.WebSocketMcpTransport
|
|||
*
|
||||
* @property session The WebSocket server session used for communication.
|
||||
*/
|
||||
class WebSocketMcpServerTransport(
|
||||
public class WebSocketMcpServerTransport(
|
||||
override val session: WebSocketServerSession,
|
||||
) : WebSocketMcpTransport() {
|
||||
override suspend fun initializeSession() {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import kotlinx.serialization.serializer
|
|||
import org.jetbrains.kotlinx.mcp.fromJSON
|
||||
import org.jetbrains.kotlinx.mcp.toJSON
|
||||
import kotlin.collections.get
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
|
@ -38,7 +39,7 @@ internal const val IMPLEMENTATION_NAME = "mcp-ktor"
|
|||
/**
|
||||
* Callback for progress notifications.
|
||||
*/
|
||||
typealias ProgressCallback = (Progress) -> Unit
|
||||
public typealias ProgressCallback = (Progress) -> Unit
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@PublishedApi
|
||||
|
|
@ -55,7 +56,7 @@ internal val McpJson: Json by lazy {
|
|||
/**
|
||||
* Additional initialization options.
|
||||
*/
|
||||
open class ProtocolOptions(
|
||||
public open class ProtocolOptions(
|
||||
/**
|
||||
* Whether to restrict emitted requests to only those that the remote side has indicated
|
||||
* that they can handle, through their advertised capabilities.
|
||||
|
|
@ -66,20 +67,20 @@ open class ProtocolOptions(
|
|||
* Currently this defaults to false, for backwards compatibility with SDK versions
|
||||
* that did not advertise capabilities correctly. In future, this will default to true.
|
||||
*/
|
||||
var enforceStrictCapabilities: Boolean = false,
|
||||
public var enforceStrictCapabilities: Boolean = false,
|
||||
|
||||
var timeout: Duration = DEFAULT_REQUEST_TIMEOUT
|
||||
public var timeout: Duration = DEFAULT_REQUEST_TIMEOUT,
|
||||
)
|
||||
|
||||
/**
|
||||
* The default request timeout.
|
||||
*/
|
||||
val DEFAULT_REQUEST_TIMEOUT: Duration = 60000.milliseconds
|
||||
public val DEFAULT_REQUEST_TIMEOUT: Duration = 60000.milliseconds
|
||||
|
||||
/**
|
||||
* Options that can be given per request.
|
||||
*/
|
||||
data class RequestOptions(
|
||||
public data class RequestOptions(
|
||||
/**
|
||||
* If set, requests progress notifications from the remote end (if supported).
|
||||
* When progress notifications are received, this callback will be invoked.
|
||||
|
|
@ -92,13 +93,13 @@ data class RequestOptions(
|
|||
*
|
||||
* If not specified, `DEFAULT_REQUEST_TIMEOUT` will be used as the timeout.
|
||||
*/
|
||||
val timeout: Duration = DEFAULT_REQUEST_TIMEOUT
|
||||
val timeout: Duration = DEFAULT_REQUEST_TIMEOUT,
|
||||
)
|
||||
|
||||
/**
|
||||
* Extra data given to request handlers.
|
||||
*/
|
||||
class RequestHandlerExtra()
|
||||
public class RequestHandlerExtra()
|
||||
|
||||
internal val COMPLETED = CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
|
||||
|
|
@ -106,16 +107,16 @@ internal val COMPLETED = CompletableDeferred(Unit).also { it.complete(Unit) }
|
|||
* Implements MCP protocol framing on top of a pluggable transport, including
|
||||
* features like request/response linking, notifications, and progress.
|
||||
*/
|
||||
abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification, SendResultT : RequestResult>(
|
||||
@PublishedApi internal val _options: ProtocolOptions?
|
||||
public abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification, SendResultT : RequestResult>(
|
||||
@PublishedApi internal val options: ProtocolOptions?,
|
||||
) {
|
||||
var transport: Transport? = null
|
||||
public var transport: Transport? = null
|
||||
private set
|
||||
|
||||
@PublishedApi
|
||||
internal val requestHandlers: MutableMap<String, suspend (request: JSONRPCRequest, extra: RequestHandlerExtra) -> SendResultT?> =
|
||||
mutableMapOf()
|
||||
val notificationHandlers: MutableMap<String, suspend (notification: JSONRPCNotification) -> Unit> =
|
||||
public val notificationHandlers: MutableMap<String, suspend (notification: JSONRPCNotification) -> Unit> =
|
||||
mutableMapOf()
|
||||
|
||||
@PublishedApi
|
||||
|
|
@ -130,24 +131,25 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
*
|
||||
* This is invoked when close() is called as well.
|
||||
*/
|
||||
open fun onclose() {}
|
||||
public open fun onclose() {}
|
||||
|
||||
/**
|
||||
* Callback for when an error occurs.
|
||||
*
|
||||
* Note that errors are not necessarily fatal they are used for reporting any kind of exceptional condition out of band.
|
||||
*/
|
||||
open fun onerror(error: Throwable) {}
|
||||
public open fun onerror(error: Throwable) {}
|
||||
|
||||
/**
|
||||
* A handler to invoke for any request types that do not have their own handler installed.
|
||||
*/
|
||||
var fallbackRequestHandler: (suspend (request: JSONRPCRequest, extra: RequestHandlerExtra) -> SendResultT?)? = null
|
||||
public var fallbackRequestHandler: (suspend (request: JSONRPCRequest, extra: RequestHandlerExtra) -> SendResultT?)? =
|
||||
null
|
||||
|
||||
/**
|
||||
* A handler to invoke for any notification types that do not have their own handler installed.
|
||||
*/
|
||||
var fallbackNotificationHandler: (suspend (notification: JSONRPCNotification) -> Unit)? = null
|
||||
public var fallbackNotificationHandler: (suspend (notification: JSONRPCNotification) -> Unit)? = null
|
||||
|
||||
init {
|
||||
setNotificationHandler<ProgressNotification>(Method.Defined.NotificationsProgress) { notification ->
|
||||
|
|
@ -166,7 +168,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
*
|
||||
* The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
|
||||
*/
|
||||
open suspend fun connect(transport: Transport) {
|
||||
public open suspend fun connect(transport: Transport) {
|
||||
this.transport = transport
|
||||
transport.onClose = {
|
||||
this.onClose()
|
||||
|
|
@ -258,13 +260,15 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
} catch (cause: Throwable) {
|
||||
LOGGER.error(cause) { "Error handling request: ${request.method} (id: ${request.id})" }
|
||||
|
||||
transport!!.send(JSONRPCResponse(
|
||||
id = request.id,
|
||||
error = JSONRPCError(
|
||||
ErrorCode.Defined.InternalError,
|
||||
message = cause.message ?: "Internal error",
|
||||
transport!!.send(
|
||||
JSONRPCResponse(
|
||||
id = request.id,
|
||||
error = JSONRPCError(
|
||||
ErrorCode.Defined.InternalError,
|
||||
message = cause.message ?: "Internal error",
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -314,7 +318,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
/**
|
||||
* Closes the connection.
|
||||
*/
|
||||
suspend fun close() {
|
||||
public suspend fun close() {
|
||||
transport?.close()
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +327,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
*
|
||||
* This should be implemented by subclasses.
|
||||
*/
|
||||
abstract fun assertCapabilityForMethod(method: Method)
|
||||
protected abstract fun assertCapabilityForMethod(method: Method)
|
||||
|
||||
/**
|
||||
* A method to check if a notification is supported by the local side, for the given method to be sent.
|
||||
|
|
@ -337,14 +341,14 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
*
|
||||
* This should be implemented by subclasses.
|
||||
*/
|
||||
abstract fun assertRequestHandlerCapability(method: Method)
|
||||
public abstract fun assertRequestHandlerCapability(method: Method)
|
||||
|
||||
/**
|
||||
* Sends a request and wait for a response.
|
||||
*
|
||||
* Do not use this method to emit notifications! Use notification() instead.
|
||||
*/
|
||||
suspend fun <T : RequestResult> request(
|
||||
public suspend fun <T : RequestResult> request(
|
||||
request: SendRequestT,
|
||||
options: RequestOptions? = null,
|
||||
): T {
|
||||
|
|
@ -352,7 +356,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
val result = CompletableDeferred<T>()
|
||||
val transport = this@Protocol.transport ?: throw Error("Not connected")
|
||||
|
||||
if (_options?.enforceStrictCapabilities == true) {
|
||||
if (this@Protocol.options?.enforceStrictCapabilities == true) {
|
||||
assertCapabilityForMethod(request.method)
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +368,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
progressHandlers[messageId] = options.onProgress
|
||||
}
|
||||
|
||||
responseHandlers.set(messageId, { response, error ->
|
||||
responseHandlers[messageId] = set@{ response, error ->
|
||||
if (error != null) {
|
||||
result.completeExceptionally(error)
|
||||
return@set
|
||||
|
|
@ -381,7 +385,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
} catch (error: Throwable) {
|
||||
result.completeExceptionally(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val cancel: suspend (Throwable) -> Unit = { reason: Throwable ->
|
||||
responseHandlers.remove(messageId)
|
||||
|
|
@ -423,7 +427,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
/**
|
||||
* Emits a notification, which is a one-way message that does not expect a response.
|
||||
*/
|
||||
suspend fun notification(notification: SendNotificationT) {
|
||||
public suspend fun notification(notification: SendNotificationT) {
|
||||
LOGGER.trace { "Sending notification: ${notification.method}" }
|
||||
val transport = this.transport ?: error("Not connected")
|
||||
assertNotificationCapability(notification.method)
|
||||
|
|
@ -440,18 +444,27 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
*
|
||||
* Note that this will replace any previous request handler for the same method.
|
||||
*/
|
||||
inline fun <reified T : Request> setRequestHandler(
|
||||
public inline fun <reified T : Request> setRequestHandler(
|
||||
method: Method,
|
||||
noinline block: suspend (T, RequestHandlerExtra) -> SendResultT?
|
||||
noinline block: suspend (T, RequestHandlerExtra) -> SendResultT?,
|
||||
) {
|
||||
setRequestHandler(typeOf<T>(), method, block)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T : Request> setRequestHandler(
|
||||
requestType: KType,
|
||||
method: Method,
|
||||
block: suspend (T, RequestHandlerExtra) -> SendResultT?,
|
||||
) {
|
||||
assertRequestHandlerCapability(method)
|
||||
|
||||
val type = typeOf<T>()
|
||||
val serializer = McpJson.serializersModule.serializer(type)
|
||||
val serializer = McpJson.serializersModule.serializer(requestType)
|
||||
|
||||
requestHandlers[method.value] = { request, extraHandler ->
|
||||
val result = request.params?.let { McpJson.decodeFromJsonElement(serializer, it) }
|
||||
val response = if (result != null) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
block(result as T, extraHandler)
|
||||
} else {
|
||||
EmptyRequestResult()
|
||||
|
|
@ -465,7 +478,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
/**
|
||||
* Removes the request handler for the given method.
|
||||
*/
|
||||
fun removeRequestHandler(method: Method) {
|
||||
public fun removeRequestHandler(method: Method) {
|
||||
requestHandlers.remove(method.value)
|
||||
}
|
||||
|
||||
|
|
@ -474,7 +487,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
*
|
||||
* Note that this will replace any previous notification handler for the same method.
|
||||
*/
|
||||
fun <T : Notification> setNotificationHandler(method: Method, handler: (notification: T) -> Deferred<Unit>) {
|
||||
public fun <T : Notification> setNotificationHandler(method: Method, handler: (notification: T) -> Deferred<Unit>) {
|
||||
this.notificationHandlers[method.value] = {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
handler(it.fromJSON() as T)
|
||||
|
|
@ -484,7 +497,7 @@ abstract class Protocol<SendRequestT : Request, SendNotificationT : Notification
|
|||
/**
|
||||
* Removes the notification handler for the given method.
|
||||
*/
|
||||
fun removeNotificationHandler(method: Method) {
|
||||
public fun removeNotificationHandler(method: Method) {
|
||||
this.notificationHandlers.remove(method.value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import kotlinx.serialization.encodeToString
|
|||
/**
|
||||
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
|
||||
*/
|
||||
class ReadBuffer {
|
||||
public class ReadBuffer {
|
||||
private val buffer: Buffer = Buffer()
|
||||
|
||||
fun append(chunk: ByteArray) {
|
||||
public fun append(chunk: ByteArray) {
|
||||
buffer.writeFully(chunk)
|
||||
}
|
||||
|
||||
fun readMessage(): JSONRPCMessage? {
|
||||
public fun readMessage(): JSONRPCMessage? {
|
||||
if (buffer.exhausted()) return null
|
||||
var lfIndex = buffer.indexOf('\n'.code.toByte())
|
||||
val line = when (lfIndex) {
|
||||
|
|
@ -41,7 +41,7 @@ class ReadBuffer {
|
|||
return deserializeMessage(line)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
public fun clear() {
|
||||
buffer.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import org.jetbrains.kotlinx.mcp.JSONRPCMessage
|
|||
/**
|
||||
* Describes the minimal contract for a MCP transport that a client or server can communicate over.
|
||||
*/
|
||||
interface Transport {
|
||||
public interface Transport {
|
||||
/**
|
||||
* Starts processing messages on the transport, including any connection steps that might need to be taken.
|
||||
*
|
||||
|
|
@ -14,24 +14,24 @@ interface Transport {
|
|||
* NOTE: This method should not be called explicitly when using Client, Server, or Protocol classes,
|
||||
* as they will implicitly call start().
|
||||
*/
|
||||
suspend fun start()
|
||||
public suspend fun start()
|
||||
|
||||
/**
|
||||
* Sends a JSON-RPC message (request or response).
|
||||
*/
|
||||
suspend fun send(message: JSONRPCMessage)
|
||||
public suspend fun send(message: JSONRPCMessage)
|
||||
|
||||
/**
|
||||
* Closes the connection.
|
||||
*/
|
||||
suspend fun close()
|
||||
public suspend fun close()
|
||||
|
||||
/**
|
||||
* Callback for when the connection is closed for any reason.
|
||||
*
|
||||
* This should be invoked when close() is called as well.
|
||||
*/
|
||||
var onClose: (() -> Unit)?
|
||||
public var onClose: (() -> Unit)?
|
||||
|
||||
/**
|
||||
* Callback for when an error occurs.
|
||||
|
|
@ -39,10 +39,10 @@ interface Transport {
|
|||
* Note that errors are not necessarily fatal; they are used for reporting any kind of
|
||||
* exceptional condition out of band.
|
||||
*/
|
||||
var onError: ((Throwable) -> Unit)?
|
||||
public var onError: ((Throwable) -> Unit)?
|
||||
|
||||
/**
|
||||
* Callback for when a message (request or response) is received over the connection.
|
||||
*/
|
||||
var onMessage: (suspend ((JSONRPCMessage) -> Unit))?
|
||||
public var onMessage: (suspend ((JSONRPCMessage) -> Unit))?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ internal const val MCP_SUBPROTOCOL = "mcp"
|
|||
* Abstract class representing a WebSocket transport for the Model Context Protocol (MCP).
|
||||
* Handles communication over a WebSocket session.
|
||||
*/
|
||||
abstract class WebSocketMcpTransport : Transport {
|
||||
public abstract class WebSocketMcpTransport : Transport {
|
||||
private val scope by lazy {
|
||||
CoroutineScope(session.coroutineContext + SupervisorJob())
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,3 @@
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import org.jetbrains.kotlinx.mcp.JSONRPCMessage
|
||||
import org.jetbrains.kotlinx.mcp.shared.Transport
|
||||
|
||||
|
|
@ -7,8 +5,6 @@ import org.jetbrains.kotlinx.mcp.shared.Transport
|
|||
* In-memory transport for creating clients and servers that talk to each other within the same process.
|
||||
*/
|
||||
class InMemoryTransport : Transport {
|
||||
private val scope = CoroutineScope(SupervisorJob())
|
||||
|
||||
private var otherTransport: InMemoryTransport? = null
|
||||
private val messageQueue: MutableList<JSONRPCMessage> = mutableListOf()
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue