kotlin-sdk/samples/kotlin-mcp-server/src/commonMain/kotlin/server.kt
Pavel Gorgulov 0cff2ca718
add ios and wasm targets (#81)
* add wasm and ios targets

* add yarn.lock file to gitignore

* clean up code

* fix typo in method name

* add wasm server example

* update jrelease publishing for klibs

* update gradle-publish workflow to use macos runner

* update README to reflect multiplatform support and clarify server setup for JVM and WASM

* remove the old main file from the kotlin-mcp-server example

* update api dump

* Refactor `build.gradle.kts`

* clean up imports and remove unnecessary charset specification in tests

* add JS target configuration and update API dump
2025-05-29 05:01:50 +00:00

151 lines
No EOL
5.2 KiB
Kotlin

package shared
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer
import io.ktor.server.response.respond
import io.ktor.server.routing.post
import io.ktor.server.routing.routing
import io.ktor.server.sse.SSE
import io.ktor.server.sse.sse
import io.ktor.util.collections.ConcurrentMap
import io.modelcontextprotocol.kotlin.sdk.CallToolResult
import io.modelcontextprotocol.kotlin.sdk.GetPromptResult
import io.modelcontextprotocol.kotlin.sdk.Implementation
import io.modelcontextprotocol.kotlin.sdk.PromptArgument
import io.modelcontextprotocol.kotlin.sdk.PromptMessage
import io.modelcontextprotocol.kotlin.sdk.ReadResourceResult
import io.modelcontextprotocol.kotlin.sdk.Role
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.TextContent
import io.modelcontextprotocol.kotlin.sdk.TextResourceContents
import io.modelcontextprotocol.kotlin.sdk.Tool
import io.modelcontextprotocol.kotlin.sdk.server.Server
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
import io.modelcontextprotocol.kotlin.sdk.server.SseServerTransport
import io.modelcontextprotocol.kotlin.sdk.server.mcp
import kotlin.collections.set
fun configureServer(): Server {
val server = Server(
Implementation(
name = "mcp-kotlin test server",
version = "0.1.0"
),
ServerOptions(
capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(listChanged = true),
resources = ServerCapabilities.Resources(subscribe = true, listChanged = true),
tools = ServerCapabilities.Tools(listChanged = true),
)
)
)
server.addPrompt(
name = "Kotlin Developer",
description = "Develop small kotlin applications",
arguments = listOf(
PromptArgument(
name = "Project Name",
description = "Project name for the new project",
required = true
)
)
) { request ->
GetPromptResult(
"Description for ${request.name}",
messages = listOf(
PromptMessage(
role = Role.user,
content = TextContent("Develop a kotlin project named <name>${request.arguments?.get("Project Name")}</name>")
)
)
)
}
// Add a tool
server.addTool(
name = "kotlin-sdk-tool",
description = "A test tool",
inputSchema = Tool.Input()
) { request ->
CallToolResult(
content = listOf(TextContent("Hello, world!"))
)
}
// Add a resource
server.addResource(
uri = "https://search.com/",
name = "Web Search",
description = "Web search engine",
mimeType = "text/html"
) { request ->
ReadResourceResult(
contents = listOf(
TextResourceContents("Placeholder content for ${request.uri}", request.uri, "text/html")
)
)
}
return server
}
suspend fun runSseMcpServerWithPlainConfiguration(port: Int): Unit {
val servers = ConcurrentMap<String, Server>()
println("Starting sse server on port $port. ")
println("Use inspector to connect to the http://localhost:$port/sse")
embeddedServer(CIO, host = "0.0.0.0", port = port) {
install(SSE)
routing {
sse("/sse") {
val transport = SseServerTransport("/message", this)
val server = configureServer()
// For SSE, you can also add prompts/tools/resources if needed:
// server.addTool(...), server.addPrompt(...), server.addResource(...)
servers[transport.sessionId] = server
server.onClose {
println("Server closed")
servers.remove(transport.sessionId)
}
server.connect(transport)
}
post("/message") {
println("Received Message")
val sessionId: String = call.request.queryParameters["sessionId"]!!
val transport = servers[sessionId]?.transport as? SseServerTransport
if (transport == null) {
call.respond(HttpStatusCode.NotFound, "Session not found")
return@post
}
transport.handlePostMessage(call)
}
}
}.startSuspend(wait = true)
}
/**
* Starts an SSE (Server Sent Events) MCP server using the Ktor framework and the specified port.
*
* The url can be accessed in the MCP inspector at [http://localhost:$port]
*
* @param port The port number on which the SSE MCP server will listen for client connections.
* @return Unit This method does not return a value.
*/
suspend fun runSseMcpServerUsingKtorPlugin(port: Int): Unit {
println("Starting sse server on port $port")
println("Use inspector to connect to the http://localhost:$port/sse")
embeddedServer(CIO, host = "0.0.0.0", port = port) {
mcp {
return@mcp configureServer()
}
}.startSuspend(wait = true)
}