# #302 Update samples build workflow - Introduce workflow to build samples independently from the main project - Keep building samples against the current snapshot version of the SDK. Update composite build configuration. - Temporarily disable "samples/kotlin-mcp-server": the multiplatform project is broken. **It doesn't compile for me even on the main branch**. Not working with [MCP Instepcor](https://modelcontextprotocol.io/docs/tools/inspector), because CORS is not configured. Deserves a separate PR to fix. ## Motivation and Context Samples should be independently buildable ## How Has This Been Tested? <!-- Have you tested this in a real application? Which scenarios were tested? --> ## Breaking Changes <!-- Will users need to update their code or configurations? --> ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [x] Build improvement - [x] Tests ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context <!-- Add any other context, implementation notes, or design decisions -->
This commit is contained in:
parent
ba18b2b95e
commit
2da14b0f2e
4 changed files with 1271 additions and 73 deletions
|
|
@ -1,23 +1,38 @@
|
|||
package io.modelcontextprotocol.sample.server
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.utils.io.streams.*
|
||||
import io.modelcontextprotocol.kotlin.sdk.*
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.plugins.defaultRequest
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import io.ktor.http.headers
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.streams.asInput
|
||||
import io.modelcontextprotocol.kotlin.sdk.CallToolResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.TextContent
|
||||
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.StdioServerTransport
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.io.Sink
|
||||
import kotlinx.io.asSink
|
||||
import kotlinx.io.buffered
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.doubleOrNull
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
|
||||
// Main function to run the MCP server
|
||||
fun `run mcp server`() {
|
||||
/**
|
||||
* Starts an MCP server that provides weather-related tools for fetching active weather alerts by state and weather forecasts by latitude/longitude.
|
||||
*/
|
||||
fun runMcpServer() {
|
||||
// Base URL for the Weather API
|
||||
val baseUrl = "https://api.weather.gov"
|
||||
|
||||
|
|
@ -33,10 +48,12 @@ fun `run mcp server`() {
|
|||
}
|
||||
// Install content negotiation plugin for JSON serialization/deserialization
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
ignoreUnknownKeys = true
|
||||
prettyPrint = true
|
||||
})
|
||||
json(
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
prettyPrint = true
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,11 +61,11 @@ fun `run mcp server`() {
|
|||
val server = Server(
|
||||
Implementation(
|
||||
name = "weather", // Tool name is "weather"
|
||||
version = "1.0.0" // Version of the implementation
|
||||
version = "1.0.0", // Version of the implementation
|
||||
),
|
||||
ServerOptions(
|
||||
capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true))
|
||||
)
|
||||
capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true)),
|
||||
),
|
||||
)
|
||||
|
||||
// Register a tool to fetch weather alerts by state
|
||||
|
|
@ -64,11 +81,11 @@ fun `run mcp server`() {
|
|||
put("description", "Two-letter US state code (e.g. CA, NY)")
|
||||
}
|
||||
},
|
||||
required = listOf("state")
|
||||
)
|
||||
required = listOf("state"),
|
||||
),
|
||||
) { request ->
|
||||
val state = request.arguments["state"]?.jsonPrimitive?.content ?: return@addTool CallToolResult(
|
||||
content = listOf(TextContent("The 'state' parameter is required."))
|
||||
content = listOf(TextContent("The 'state' parameter is required.")),
|
||||
)
|
||||
|
||||
val alerts = httpClient.getAlerts(state)
|
||||
|
|
@ -91,14 +108,14 @@ fun `run mcp server`() {
|
|||
put("type", "number")
|
||||
}
|
||||
},
|
||||
required = listOf("latitude", "longitude")
|
||||
)
|
||||
required = listOf("latitude", "longitude"),
|
||||
),
|
||||
) { request ->
|
||||
val latitude = request.arguments["latitude"]?.jsonPrimitive?.doubleOrNull
|
||||
val longitude = request.arguments["longitude"]?.jsonPrimitive?.doubleOrNull
|
||||
if (latitude == null || longitude == null) {
|
||||
return@addTool CallToolResult(
|
||||
content = listOf(TextContent("The 'latitude' and 'longitude' parameters are required."))
|
||||
content = listOf(TextContent("The 'latitude' and 'longitude' parameters are required.")),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +127,7 @@ fun `run mcp server`() {
|
|||
// Create a transport using standard IO for server communication
|
||||
val transport = StdioServerTransport(
|
||||
System.`in`.asInput(),
|
||||
System.out.asSink().buffered()
|
||||
System.out.asSink().buffered(),
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package io.modelcontextprotocol.sample.server
|
||||
|
||||
fun main() = `run mcp server`()
|
||||
fun main() = runMcpServer()
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@ import kotlinx.io.asSource
|
|||
import kotlinx.io.buffered
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
|
||||
fun main(): Unit = runBlocking {
|
||||
val process = ProcessBuilder("java", "-jar", "build/libs/weather-stdio-server-0.1.0-all.jar")
|
||||
val process = ProcessBuilder("java", "-jar", "./build/libs/weather-stdio-server-0.1.0-all.jar")
|
||||
.start()
|
||||
|
||||
val transport = StdioClientTransport(
|
||||
input = process.inputStream.asSource().buffered(),
|
||||
output = process.outputStream.asSink().buffered()
|
||||
output = process.outputStream.asSink().buffered(),
|
||||
)
|
||||
|
||||
// Initialize the MCP client with client information
|
||||
|
|
@ -29,15 +29,17 @@ fun main(): Unit = runBlocking {
|
|||
|
||||
client.connect(transport)
|
||||
|
||||
|
||||
val toolsList = client.listTools()?.tools?.map { it.name }
|
||||
val toolsList = client.listTools().tools.map { it.name }
|
||||
println("Available Tools = $toolsList")
|
||||
|
||||
val weatherForecastResult = client.callTool(
|
||||
CallToolRequest(
|
||||
name = "get_forecast",
|
||||
arguments = JsonObject(mapOf("latitude" to JsonPrimitive(38.5816), "longitude" to JsonPrimitive(-121.4944)))
|
||||
)
|
||||
arguments = buildJsonObject {
|
||||
put("latitude", JsonPrimitive(38.5816))
|
||||
put("longitude", JsonPrimitive(-121.4944))
|
||||
},
|
||||
),
|
||||
)?.content?.map { if (it is TextContent) it.text else it.toString() }
|
||||
|
||||
println("Weather Forcast: ${weatherForecastResult?.joinToString(separator = "\n", prefix = "\n", postfix = "\n")}")
|
||||
|
|
@ -46,11 +48,11 @@ fun main(): Unit = runBlocking {
|
|||
client.callTool(
|
||||
CallToolRequest(
|
||||
name = "get_alerts",
|
||||
arguments = JsonObject(mapOf("state" to JsonPrimitive("TX")))
|
||||
)
|
||||
arguments = JsonObject(mapOf("state" to JsonPrimitive("TX"))),
|
||||
),
|
||||
)?.content?.map { if (it is TextContent) it.text else it.toString() }
|
||||
|
||||
println("Alert Response = $alertResult")
|
||||
|
||||
client.close()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package io.modelcontextprotocol.sample.client
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
class FakeTest {
|
||||
|
||||
@Test
|
||||
fun testSomething() {
|
||||
assert(true) { "Bingo!" }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue