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 ${request.arguments?.get("Project 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() 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) }