1+ package shared
2+
3+ import io.ktor.http.HttpStatusCode
4+ import io.ktor.server.application.install
5+ import io.ktor.server.cio.CIO
6+ import io.ktor.server.engine.embeddedServer
7+ import io.ktor.server.response.respond
8+ import io.ktor.server.routing.post
9+ import io.ktor.server.routing.routing
10+ import io.ktor.server.sse.SSE
11+ import io.ktor.server.sse.sse
12+ import io.ktor.util.collections.ConcurrentMap
13+ import io.modelcontextprotocol.kotlin.sdk.CallToolResult
14+ import io.modelcontextprotocol.kotlin.sdk.GetPromptResult
15+ import io.modelcontextprotocol.kotlin.sdk.Implementation
16+ import io.modelcontextprotocol.kotlin.sdk.PromptArgument
17+ import io.modelcontextprotocol.kotlin.sdk.PromptMessage
18+ import io.modelcontextprotocol.kotlin.sdk.ReadResourceResult
19+ import io.modelcontextprotocol.kotlin.sdk.Role
20+ import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
21+ import io.modelcontextprotocol.kotlin.sdk.TextContent
22+ import io.modelcontextprotocol.kotlin.sdk.TextResourceContents
23+ import io.modelcontextprotocol.kotlin.sdk.Tool
24+ import io.modelcontextprotocol.kotlin.sdk.server.Server
25+ import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
26+ import io.modelcontextprotocol.kotlin.sdk.server.SseServerTransport
27+ import io.modelcontextprotocol.kotlin.sdk.server.mcp
28+ import kotlin.collections.set
29+
30+ fun configureServer (): Server {
31+ val server = Server (
32+ Implementation (
33+ name = " mcp-kotlin test server" ,
34+ version = " 0.1.0"
35+ ),
36+ ServerOptions (
37+ capabilities = ServerCapabilities (
38+ prompts = ServerCapabilities .Prompts (listChanged = true ),
39+ resources = ServerCapabilities .Resources (subscribe = true , listChanged = true ),
40+ tools = ServerCapabilities .Tools (listChanged = true ),
41+ )
42+ )
43+ )
44+
45+ server.addPrompt(
46+ name = " Kotlin Developer" ,
47+ description = " Develop small kotlin applications" ,
48+ arguments = listOf (
49+ PromptArgument (
50+ name = " Project Name" ,
51+ description = " Project name for the new project" ,
52+ required = true
53+ )
54+ )
55+ ) { request ->
56+ GetPromptResult (
57+ " Description for ${request.name} " ,
58+ messages = listOf (
59+ PromptMessage (
60+ role = Role .user,
61+ content = TextContent (" Develop a kotlin project named <name>${request.arguments?.get(" Project Name" )} </name>" )
62+ )
63+ )
64+ )
65+ }
66+
67+ // Add a tool
68+ server.addTool(
69+ name = " kotlin-sdk-tool" ,
70+ description = " A test tool" ,
71+ inputSchema = Tool .Input ()
72+ ) { request ->
73+ CallToolResult (
74+ content = listOf (TextContent (" Hello, world!" ))
75+ )
76+ }
77+
78+ // Add a resource
79+ server.addResource(
80+ uri = " https://search.com/" ,
81+ name = " Web Search" ,
82+ description = " Web search engine" ,
83+ mimeType = " text/html"
84+ ) { request ->
85+ ReadResourceResult (
86+ contents = listOf (
87+ TextResourceContents (" Placeholder content for ${request.uri} " , request.uri, " text/html" )
88+ )
89+ )
90+ }
91+
92+ return server
93+ }
94+
95+ suspend fun runSseMcpServerWithPlainConfiguration (port : Int ): Unit {
96+ val servers = ConcurrentMap <String , Server >()
97+ println (" Starting sse server on port $port . " )
98+ println (" Use inspector to connect to the http://localhost:$port /sse" )
99+
100+ embeddedServer(CIO , host = " 0.0.0.0" , port = port) {
101+ install(SSE )
102+ routing {
103+ sse(" /sse" ) {
104+ val transport = SseServerTransport (" /message" , this )
105+ val server = configureServer()
106+
107+ // For SSE, you can also add prompts/tools/resources if needed:
108+ // server.addTool(...), server.addPrompt(...), server.addResource(...)
109+
110+ servers[transport.sessionId] = server
111+
112+ server.onClose {
113+ println (" Server closed" )
114+ servers.remove(transport.sessionId)
115+ }
116+
117+ server.connect(transport)
118+ }
119+ post(" /message" ) {
120+ println (" Received Message" )
121+ val sessionId: String = call.request.queryParameters[" sessionId" ]!!
122+ val transport = servers[sessionId]?.transport as ? SseServerTransport
123+ if (transport == null ) {
124+ call.respond(HttpStatusCode .NotFound , " Session not found" )
125+ return @post
126+ }
127+
128+ transport.handlePostMessage(call)
129+ }
130+ }
131+ }.startSuspend(wait = true )
132+ }
133+
134+ /* *
135+ * Starts an SSE (Server Sent Events) MCP server using the Ktor framework and the specified port.
136+ *
137+ * The url can be accessed in the MCP inspector at [http://localhost:$port]
138+ *
139+ * @param port The port number on which the SSE MCP server will listen for client connections.
140+ * @return Unit This method does not return a value.
141+ */
142+ suspend fun runSseMcpServerUsingKtorPlugin (port : Int ): Unit {
143+ println (" Starting sse server on port $port " )
144+ println (" Use inspector to connect to the http://localhost:$port /sse" )
145+
146+ embeddedServer(CIO , host = " 0.0.0.0" , port = port) {
147+ mcp {
148+ return @mcp configureServer()
149+ }
150+ }.startSuspend(wait = true )
151+ }
0 commit comments