This is an automated email from the ASF dual-hosted git repository.

rustyrazorblade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cassandra-easy-stress.git


The following commit(s) were added to refs/heads/main by this push:
     new 3b12228  [MCP] Add Streamable HTTP transport (#106)
3b12228 is described below

commit 3b1222846d4a99d2d18f65de3588bd919ac6187b
Author: Yuki Morishita <[email protected]>
AuthorDate: Sat Oct 10 04:26:34 2026 +0900

    [MCP] Add Streamable HTTP transport (#106)
    
    Patch by Yuki Morishita, Reviewed by Jon Haddad for #106
---
 README.md                                          |   2 +-
 build.gradle.kts                                   |   4 +
 gradle/libs.versions.toml                          |   2 +-
 .../apache/cassandra/easystress/commands/Server.kt |  77 +++++-----
 .../easystress/server/StressTestManager.kt         |  19 +--
 .../easystress/server/ToolInputSchemaGenerator.kt  |  10 +-
 .../easystress/server/tools/FieldsTool.kt          |  15 +-
 .../easystress/server/tools/ListWorkloadsTool.kt   |  15 +-
 .../easystress/server/tools/WorkloadInfoTool.kt    |  17 ++-
 .../cassandra/easystress/commands/MCPServerTest.kt | 149 ++++++++++++++++---
 .../easystress/server/StressTestManagerTest.kt     | 160 +++++++++++----------
 .../easystress/server/tools/FieldsToolTest.kt      |  30 ++--
 .../server/tools/ListWorkloadsToolTest.kt          |  26 ++--
 .../server/tools/WorkloadInfoToolTest.kt           |  34 ++---
 14 files changed, 344 insertions(+), 216 deletions(-)

diff --git a/README.md b/README.md
index bdf60b0..8d78d20 100644
--- a/README.md
+++ b/README.md
@@ -345,7 +345,7 @@ To test the MCP server integration with Claude Code:
 
 1. Add the MCP server (assuming it's running locally):
    ```bash
-   claude mcp add -t sse cassandra-easy-stress http://localhost:9000/sse
+   claude mcp add -t http cassandra-easy-stress http://localhost:9000/mcp
    ```
 
 2. Run the MCP integration test prompt:
diff --git a/build.gradle.kts b/build.gradle.kts
index 160320a..e6f806d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
 import com.netflix.gradle.plugins.deb.Deb
 import com.netflix.gradle.plugins.rpm.Rpm
 import io.gitlab.arturbosch.detekt.Detekt
@@ -346,3 +347,6 @@ tasks.withType<Detekt>().configureEach {
 tasks.withType<DetektCreateBaselineTask>().configureEach {
     jvmTarget = "21"
 }
+tasks.withType<ShadowJar>().configureEach {
+    isZip64 = true
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5d9f65f..949d613 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -32,7 +32,7 @@ junit-params = "6.1.3"
 assertj = "3.9.1"
 mockk = "1.14.3"
 coroutines-test = "1.9.0"
-mcp-sdk = "0.7.2"
+mcp-sdk = "0.15.0"
 ktor = "3.3.0"
 testcontainers = "2.0.5"
 
diff --git a/src/main/kotlin/org/apache/cassandra/easystress/commands/Server.kt 
b/src/main/kotlin/org/apache/cassandra/easystress/commands/Server.kt
index bc29299..45fcabc 100644
--- a/src/main/kotlin/org/apache/cassandra/easystress/commands/Server.kt
+++ b/src/main/kotlin/org/apache/cassandra/easystress/commands/Server.kt
@@ -19,24 +19,19 @@ package org.apache.cassandra.easystress.commands
 
 import com.beust.jcommander.Parameter
 import com.beust.jcommander.Parameters
-import io.ktor.serialization.kotlinx.json.json
-import io.ktor.server.application.install
 import io.ktor.server.cio.CIO
 import io.ktor.server.cio.CIOApplicationEngine
 import io.ktor.server.engine.EmbeddedServer
 import io.ktor.server.engine.embeddedServer
-import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
 import io.ktor.server.response.respondText
 import io.ktor.server.routing.get
 import io.ktor.server.routing.routing
-import io.ktor.server.sse.heartbeat
 import io.ktor.sse.ServerSentEvent
-import io.modelcontextprotocol.kotlin.sdk.Implementation
-import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
 import io.modelcontextprotocol.kotlin.sdk.server.Server
 import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
-import io.modelcontextprotocol.kotlin.sdk.server.mcp
-import kotlinx.serialization.json.Json
+import io.modelcontextprotocol.kotlin.sdk.server.mcpStreamableHttp
+import io.modelcontextprotocol.kotlin.sdk.types.Implementation
+import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
 import org.apache.cassandra.easystress.server.StressTestManager
 import org.apache.cassandra.easystress.server.tools.FieldsTool
 import org.apache.cassandra.easystress.server.tools.ListWorkloadsTool
@@ -58,7 +53,7 @@ import kotlin.time.Duration.Companion.seconds
  *
  * The server provides:
  * - HTTP endpoint on configurable port (default: 9000)
- * - Server-Sent Events (SSE) for MCP communication
+ * - Streamable HTTP on `/mcp` for MCP communication
  * - JSON-based tool invocation and responses
  * - Thread-safe execution of concurrent tool calls
  *
@@ -72,7 +67,7 @@ import kotlin.time.Duration.Companion.seconds
  *
  * Usage:
  * ```
- * cassandra-easy-stress mcp [-p 9000]
+ * cassandra-easy-stress server [-p 9000]
  * ```
  *
  * The server runs indefinitely until interrupted. Use Ctrl+C or call stop() 
to terminate.
@@ -140,18 +135,14 @@ class Server : IStressCommand {
         )
 
     /**
-     * Creates and configures the embedded Ktor server.
+     * Creates an MCP server instance with all available tools registered.
      *
-     * Configures:
-     * - CIO engine for async I/O
-     * - JSON content negotiation with lenient parsing
-     * - Basic HTTP route for health checks
-     * - MCP protocol support with SSE transport
-     * - Registration of all available tools
+     * The Streamable HTTP transport calls this factory, possibly more than 
once.  Every
+     * instance shares [tools], so all clients see the same 
[StressTestManager].
      *
-     * @return Configured but not yet started embedded server instance
+     * @return MCP server advertising the tools capability
      */
-    private fun getServer(): EmbeddedServer<CIOApplicationEngine, 
CIOApplicationEngine.Configuration> {
+    private fun createMcpServer(): Server {
         val server =
             Server(
                 serverInfo =
@@ -174,19 +165,32 @@ class Server : IStressCommand {
             )
 
         // Register all tools with the server
-        server.addTools(
-            tools,
-        )
+        server.addTools(tools)
+        return server
+    }
 
-        return embeddedServer(CIO, port = port) {
-            install(ContentNegotiation) {
-                json(
-                    Json {
-                        prettyPrint = true
-                        isLenient = true
-                        ignoreUnknownKeys = true
-                    },
-                )
+    /**
+     * Creates and configures the embedded Ktor server.
+     *
+     * Configures:
+     * - CIO engine for async I/O
+     * - MCP Streamable HTTP transport on `/mcp`, with an SSE heartbeat every 
second
+     * - Basic HTTP route on `/` for health checks
+     *
+     * @return Configured but not yet started embedded server instance
+     */
+    private fun getServer(): EmbeddedServer<CIOApplicationEngine, 
CIOApplicationEngine.Configuration> =
+        embeddedServer(CIO, port = port) {
+            // Streamable HTTP on /mcp
+            mcpStreamableHttp(
+                path = "/mcp",
+                sseHeartbeatConfig = {
+                    period = 1.seconds
+                    // Clients parse every data field as JSON-RPC, so the 
heartbeat must be a comment.
+                    event = ServerSentEvent(comments = "heartbeat")
+                },
+            ) {
+                createMcpServer()
             }
 
             routing {
@@ -194,16 +198,5 @@ class Server : IStressCommand {
                     call.respondText("MCP Server is running")
                 }
             }
-
-            // MCP server configuration with SSE
-            mcp {
-                heartbeat {
-                    period = 1.seconds
-                    event = ServerSentEvent("heartbeat")
-                }
-                // Register all tools
-                server
-            }
         }
-    }
 }
diff --git 
a/src/main/kotlin/org/apache/cassandra/easystress/server/StressTestManager.kt 
b/src/main/kotlin/org/apache/cassandra/easystress/server/StressTestManager.kt
index 598bb35..cdad497 100644
--- 
a/src/main/kotlin/org/apache/cassandra/easystress/server/StressTestManager.kt
+++ 
b/src/main/kotlin/org/apache/cassandra/easystress/server/StressTestManager.kt
@@ -17,13 +17,13 @@
  */
 package org.apache.cassandra.easystress.server
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.TextContent
-import io.modelcontextprotocol.kotlin.sdk.Tool
-import io.modelcontextprotocol.kotlin.sdk.Tool.Input
 import io.modelcontextprotocol.kotlin.sdk.server.RegisteredTool
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
+import io.modelcontextprotocol.kotlin.sdk.types.Tool
+import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.Json
@@ -268,7 +268,8 @@ class StressTestManager {
             handler = { request: CallToolRequest ->
                 withContext(Dispatchers.IO) {
                     try {
-                        val runCommand = 
Json.decodeFromJsonElement(Run.serializer(), request.arguments)
+                        val arguments = request.params.arguments ?: 
EmptyJsonObject
+                        val runCommand = 
Json.decodeFromJsonElement(Run.serializer(), arguments)
                         if (tryStart(runCommand)) {
                             val jobId = getCurrentJobId() ?: "unknown"
                             println("Job started: $jobId")
@@ -326,7 +327,7 @@ class StressTestManager {
                     name = "stop",
                     title = null,
                     description = "Stop a currently running stress test",
-                    inputSchema = Input(properties = EmptyJsonObject),
+                    inputSchema = ToolSchema(properties = EmptyJsonObject),
                     outputSchema = null,
                     annotations = null,
                 ),
@@ -378,7 +379,7 @@ class StressTestManager {
                     name = "status",
                     title = null,
                     description = "Get status of running stress tests.  
Latency numbers are expressed in microseconds.",
-                    inputSchema = Input(properties = EmptyJsonObject),
+                    inputSchema = ToolSchema(properties = EmptyJsonObject),
                     outputSchema = null,
                     annotations = null,
                 ),
diff --git 
a/src/main/kotlin/org/apache/cassandra/easystress/server/ToolInputSchemaGenerator.kt
 
b/src/main/kotlin/org/apache/cassandra/easystress/server/ToolInputSchemaGenerator.kt
index 259724f..17d392a 100644
--- 
a/src/main/kotlin/org/apache/cassandra/easystress/server/ToolInputSchemaGenerator.kt
+++ 
b/src/main/kotlin/org/apache/cassandra/easystress/server/ToolInputSchemaGenerator.kt
@@ -19,7 +19,7 @@ package org.apache.cassandra.easystress.server
 
 import com.beust.jcommander.DynamicParameter
 import com.beust.jcommander.Parameter
-import io.modelcontextprotocol.kotlin.sdk.Tool
+import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
 import kotlinx.serialization.json.JsonPrimitive
 import kotlinx.serialization.json.buildJsonObject
 import kotlinx.serialization.json.put
@@ -30,10 +30,10 @@ import kotlin.reflect.full.memberProperties
 import kotlin.reflect.jvm.javaField
 
 /**
- * Generates MCP Tool.Input schemas from IStressCommand classes.
+ * Generates MCP ToolSchema from IStressCommand classes.
  *
  * Directly reads @Parameter and @DynamicParameter annotations using Kotlin 
reflection
- * and converts them to MCP Tool.Input format. No intermediate data structures.
+ * and converts them to MCP ToolSchema format. No intermediate data structures.
  *
  * Usage:
  * ```kotlin
@@ -44,7 +44,7 @@ import kotlin.reflect.jvm.javaField
 class ToolInputSchemaGenerator(
     private val commandClass: KClass<out IStressCommand>,
 ) {
-    fun generateToolInput(): Tool.Input {
+    fun generateToolInput(): ToolSchema {
         val commandInstance = createInstance()
         val requiredFields = mutableListOf<String>()
 
@@ -102,7 +102,7 @@ class ToolInputSchemaGenerator(
                 }
             }
 
-        return Tool.Input(
+        return ToolSchema(
             properties = properties,
             required = requiredFields,
         )
diff --git 
a/src/main/kotlin/org/apache/cassandra/easystress/server/tools/FieldsTool.kt 
b/src/main/kotlin/org/apache/cassandra/easystress/server/tools/FieldsTool.kt
index ebf0462..a59fe8a 100644
--- a/src/main/kotlin/org/apache/cassandra/easystress/server/tools/FieldsTool.kt
+++ b/src/main/kotlin/org/apache/cassandra/easystress/server/tools/FieldsTool.kt
@@ -17,12 +17,13 @@
  */
 package org.apache.cassandra.easystress.server.tools
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.TextContent
-import io.modelcontextprotocol.kotlin.sdk.Tool
 import io.modelcontextprotocol.kotlin.sdk.server.RegisteredTool
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
+import io.modelcontextprotocol.kotlin.sdk.types.Tool
+import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.addJsonObject
@@ -65,11 +66,11 @@ val FieldsTool =
                 name = "fields",
                 title = null,
                 description = "List all available field generators",
-                inputSchema = Tool.Input(properties = EmptyJsonObject),
+                inputSchema = ToolSchema(properties = EmptyJsonObject),
                 outputSchema = null,
                 annotations = null,
             ),
-        handler = ::handleFields,
+        handler = { request -> handleFields(request) },
     )
 
 private suspend fun handleFields(request: CallToolRequest): CallToolResult =
diff --git 
a/src/main/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsTool.kt
 
b/src/main/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsTool.kt
index 03c2ae7..3233744 100644
--- 
a/src/main/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsTool.kt
+++ 
b/src/main/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsTool.kt
@@ -17,12 +17,13 @@
  */
 package org.apache.cassandra.easystress.server.tools
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.TextContent
-import io.modelcontextprotocol.kotlin.sdk.Tool
 import io.modelcontextprotocol.kotlin.sdk.server.RegisteredTool
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
+import io.modelcontextprotocol.kotlin.sdk.types.Tool
+import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.addJsonObject
@@ -63,11 +64,11 @@ val ListWorkloadsTool =
                 name = "list_workloads",
                 title = null,
                 description = "List all available workloads",
-                inputSchema = Tool.Input(properties = EmptyJsonObject),
+                inputSchema = ToolSchema(properties = EmptyJsonObject),
                 outputSchema = null,
                 annotations = null,
             ),
-        handler = ::handleListWorkloads,
+        handler = { request -> handleListWorkloads(request) },
     )
 
 private suspend fun handleListWorkloads(request: CallToolRequest): 
CallToolResult =
diff --git 
a/src/main/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoTool.kt
 
b/src/main/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoTool.kt
index 200016d..c4c3822 100644
--- 
a/src/main/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoTool.kt
+++ 
b/src/main/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoTool.kt
@@ -17,11 +17,13 @@
  */
 package org.apache.cassandra.easystress.server.tools
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.TextContent
-import io.modelcontextprotocol.kotlin.sdk.Tool
 import io.modelcontextprotocol.kotlin.sdk.server.RegisteredTool
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
+import io.modelcontextprotocol.kotlin.sdk.types.Tool
+import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.Serializable
@@ -85,7 +87,7 @@ val WorkloadInfoTool =
                 title = null,
                 description = "Get details of a specific workload",
                 inputSchema =
-                    Tool.Input(
+                    ToolSchema(
                         properties =
                             buildJsonObject {
                                 putJsonObject("workload") {
@@ -98,12 +100,13 @@ val WorkloadInfoTool =
                 outputSchema = null,
                 annotations = null,
             ),
-        handler = ::handleWorkloadInfo,
+        handler = { request -> handleWorkloadInfo(request) },
     )
 
 private suspend fun handleWorkloadInfo(request: CallToolRequest): 
CallToolResult =
     try {
-        val workloadName = 
Json.decodeFromJsonElement<WorkloadInfoRequest>(request.arguments).workload
+        val arguments = request.params.arguments ?: EmptyJsonObject
+        val workloadName = 
Json.decodeFromJsonElement<WorkloadInfoRequest>(arguments).workload
 
         logger.info { "Info request for $workloadName" }
 
diff --git 
a/src/test/kotlin/org/apache/cassandra/easystress/commands/MCPServerTest.kt 
b/src/test/kotlin/org/apache/cassandra/easystress/commands/MCPServerTest.kt
index fdaf309..347d52e 100644
--- a/src/test/kotlin/org/apache/cassandra/easystress/commands/MCPServerTest.kt
+++ b/src/test/kotlin/org/apache/cassandra/easystress/commands/MCPServerTest.kt
@@ -20,9 +20,10 @@ package org.apache.cassandra.easystress.commands
 import com.beust.jcommander.DynamicParameter
 import com.beust.jcommander.Parameter
 import com.beust.jcommander.Parameters
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.TextContent
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.addJsonObject
 import kotlinx.serialization.json.buildJsonObject
@@ -91,7 +92,7 @@ class MCPServerTest {
     }
 
     @Test
-    fun `should have MCP SSE endpoint`() {
+    fun `should have MCP Streamable HTTP endpoint`() {
         server = Server()
         server!!.port = 8182
 
@@ -108,9 +109,8 @@ class MCPServerTest {
         // Give the server a moment to start
         Thread.sleep(2000)
 
-        // The MCP SDK automatically sets up SSE at the root with proper 
headers
-        // We just verify the server is running, as the MCP endpoint is 
handled by the SDK
-        val serverRunning =
+        // Verify root endpoint
+        val rootRunning =
             try {
                 val url = URL("http://localhost:8182/";)
                 val connection = url.openConnection() as HttpURLConnection
@@ -123,14 +123,116 @@ class MCPServerTest {
             } catch (e: Exception) {
                 false
             }
+        assertThat(rootRunning).isTrue()
 
-        assertThat(serverRunning).isTrue()
+        // Verify /mcp endpoint responds
+        val mcpEndpointReachable =
+            try {
+                val url = URL("http://localhost:8182/mcp";)
+                val connection = url.openConnection() as HttpURLConnection
+                connection.requestMethod = "GET"
+                connection.connectTimeout = 1000
+                connection.readTimeout = 1000
+                val responseCode = connection.responseCode
+                connection.disconnect()
+                // GET on /mcp without session or query may return 400 or 405 
or 200, which confirms the route is active
+                responseCode > 0
+            } catch (e: Exception) {
+                false
+            }
+        assertThat(mcpEndpointReachable).isTrue()
 
         // Stop the server
         server!!.stop()
         serverThread.join(5000)
     }
 
+    @Test
+    fun `GET stream should carry heartbeats as comments and only JSON-RPC in 
data fields`() {
+        server = Server()
+        server!!.port = 8183
+
+        val serverThread =
+            Thread {
+                try {
+                    server!!.execute()
+                } catch (e: Exception) {
+                    // Expected when stopping
+                }
+            }
+        serverThread.start()
+
+        // Give the server a moment to start
+        Thread.sleep(2000)
+
+        val initialize =
+            mcpPost(
+                8183,
+                null,
+                
"""{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","""
 +
+                    
""""capabilities":{},"clientInfo":{"name":"test","version":"1"}}}""",
+            )
+        val sessionId = initialize.getHeaderField("mcp-session-id")
+        initialize.disconnect()
+        assertThat(sessionId).isNotBlank()
+
+        val initialized = mcpPost(8183, sessionId, 
"""{"jsonrpc":"2.0","method":"notifications/initialized"}""")
+        assertThat(initialized.responseCode).isEqualTo(202)
+        initialized.disconnect()
+
+        val stream = URL("http://localhost:8183/mcp";).openConnection() as 
HttpURLConnection
+        stream.requestMethod = "GET"
+        stream.setRequestProperty("Accept", "text/event-stream")
+        stream.setRequestProperty("Mcp-Session-Id", sessionId)
+        stream.setRequestProperty("MCP-Protocol-Version", "2025-06-18")
+        stream.connectTimeout = 1000
+        stream.readTimeout = 3000
+        assertThat(stream.responseCode).isEqualTo(200)
+
+        // Strict clients parse every data field as a JSON-RPC message, so a 
heartbeat
+        // sent as data makes them drop the connection.
+        val dataFields = mutableListOf<String>()
+        var heartbeats = 0
+        val deadline = System.currentTimeMillis() + 3000
+        stream.inputStream.bufferedReader().use { reader ->
+            while (heartbeats < 2 && System.currentTimeMillis() < deadline) {
+                val line = reader.readLine() ?: break
+                when {
+                    line.startsWith("data:") -> 
dataFields.add(line.removePrefix("data:").trim())
+                    line.startsWith(":") -> heartbeats++
+                }
+            }
+        }
+        stream.disconnect()
+
+        assertThat(heartbeats).isGreaterThanOrEqualTo(2)
+        assertThat(dataFields.filter { it.isNotEmpty() }).allSatisfy { data ->
+            val message = Json.parseToJsonElement(data).jsonObject
+            
assertThat(message["jsonrpc"]?.jsonPrimitive?.content).isEqualTo("2.0")
+        }
+
+        server!!.stop()
+        serverThread.join(5000)
+    }
+
+    private fun mcpPost(
+        port: Int,
+        sessionId: String?,
+        body: String,
+    ): HttpURLConnection {
+        val connection = URL("http://localhost:$port/mcp";).openConnection() as 
HttpURLConnection
+        connection.requestMethod = "POST"
+        connection.doOutput = true
+        connection.setRequestProperty("Content-Type", "application/json")
+        connection.setRequestProperty("Accept", "application/json, 
text/event-stream")
+        connection.setRequestProperty("MCP-Protocol-Version", "2025-06-18")
+        sessionId?.let { connection.setRequestProperty("Mcp-Session-Id", it) }
+        connection.connectTimeout = 1000
+        connection.readTimeout = 5000
+        connection.outputStream.use { it.write(body.toByteArray()) }
+        return connection
+    }
+
     @Test
     fun `list_workloads tool handler should return CallToolResult with JSON 
content`() {
         // This test directly tests the tool handler logic
@@ -177,8 +279,11 @@ class MCPServerTest {
         // Create a mock request
         val mockRequest =
             CallToolRequest(
-                name = "list_workloads",
-                arguments = buildJsonObject {},
+                params =
+                    CallToolRequestParams(
+                        name = "list_workloads",
+                        arguments = buildJsonObject {},
+                    ),
             )
 
         // Call the handler
@@ -186,7 +291,7 @@ class MCPServerTest {
 
         // Verify the result
         assertThat(result).isNotNull()
-        assertThat(result.isError).isFalse()
+        assertThat(result.isError ?: false).isFalse()
         assertThat(result.content).isNotEmpty()
 
         // Verify the content is valid JSON
@@ -277,11 +382,14 @@ class MCPServerTest {
             val firstWorkload = workloads.entries.first().key
             val mockRequest =
                 CallToolRequest(
-                    name = "info",
-                    arguments =
-                        buildJsonObject {
-                            put("workload", firstWorkload)
-                        },
+                    params =
+                        CallToolRequestParams(
+                            name = "info",
+                            arguments =
+                                buildJsonObject {
+                                    put("workload", firstWorkload)
+                                },
+                        ),
                 )
 
             val result = toolHandler(mockRequest)
@@ -350,8 +458,11 @@ class MCPServerTest {
         // Create a mock request
         val mockRequest =
             CallToolRequest(
-                name = "fields",
-                arguments = buildJsonObject {},
+                params =
+                    CallToolRequestParams(
+                        name = "fields",
+                        arguments = buildJsonObject {},
+                    ),
             )
 
         // Call the handler
@@ -359,7 +470,7 @@ class MCPServerTest {
 
         // Verify the result
         assertThat(result).isNotNull()
-        assertThat(result.isError).isFalse()
+        assertThat(result.isError ?: false).isFalse()
         assertThat(result.content).isNotEmpty()
 
         // Verify the content is valid JSON with generators
diff --git 
a/src/test/kotlin/org/apache/cassandra/easystress/server/StressTestManagerTest.kt
 
b/src/test/kotlin/org/apache/cassandra/easystress/server/StressTestManagerTest.kt
index 7630f68..25f06b2 100644
--- 
a/src/test/kotlin/org/apache/cassandra/easystress/server/StressTestManagerTest.kt
+++ 
b/src/test/kotlin/org/apache/cassandra/easystress/server/StressTestManagerTest.kt
@@ -17,8 +17,10 @@
  */
 package org.apache.cassandra.easystress.server
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
+import io.mockk.mockk
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.withContext
@@ -43,8 +45,8 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("duration", JsonPrimitive(10))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            manager.getRunTool().handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            manager.getRunTool().handler(mockk(relaxed = true), request)
 
             // Should be running
             assertThat(manager.isRunning()).isTrue()
@@ -69,8 +71,8 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("duration", JsonPrimitive(10))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            manager.getRunTool().handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            manager.getRunTool().handler(mockk(relaxed = true), request)
 
             // Should have a thread
             assertThat(manager.getCurrentThread()).isNotNull()
@@ -96,8 +98,8 @@ class StressTestManagerTest {
                     put("duration", JsonPrimitive(10))
                     put("threads", JsonPrimitive(2))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            manager.getRunTool().handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            manager.getRunTool().handler(mockk(relaxed = true), request)
 
             // Should have a command
             val command = manager.getCurrentCommand()
@@ -128,8 +130,8 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("duration", JsonPrimitive(10))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            manager.getRunTool().handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            manager.getRunTool().handler(mockk(relaxed = true), request)
 
             val afterStart = System.currentTimeMillis()
 
@@ -158,14 +160,17 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("duration", JsonPrimitive(10))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            manager.getRunTool().handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            manager.getRunTool().handler(mockk(relaxed = true), request)
 
             // Should be running
             assertThat(manager.getStatus()).isEqualTo("running")
 
             // Stop the test
-            manager.getStopTool().handler(CallToolRequest(name = "stop", 
arguments = EmptyJsonObject))
+            manager.getStopTool().handler(
+                mockk(relaxed = true),
+                CallToolRequest(CallToolRequestParams(name = "stop", arguments 
= EmptyJsonObject)),
+            )
 
             // Should be stopped
             assertThat(manager.getStatus()).isEqualTo("stopped")
@@ -183,8 +188,8 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("duration", JsonPrimitive(10))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            manager.getRunTool().handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            manager.getRunTool().handler(mockk(relaxed = true), request)
 
             // Verify state is populated
             assertThat(manager.isRunning()).isTrue()
@@ -217,11 +222,11 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("iterations", JsonPrimitive(1))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            val result = runTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            val result = runTool.handler(mockk(relaxed = true), request)
 
             // Should start successfully (background thread will fail when 
connecting, but that's expected in unit test)
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
 
             // Clean up - stop the manager to release lock
@@ -241,15 +246,15 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("iterations", JsonPrimitive(1))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
 
             // First call should succeed
-            val result1 = runTool.handler(request)
-            assertThat(result1.isError).isFalse()
+            val result1 = runTool.handler(mockk(relaxed = true), request)
+            assertThat(result1.isError ?: false).isFalse()
 
             // Second call should fail with "already running" error
-            val result2 = runTool.handler(request)
-            assertThat(result2.isError).isTrue()
+            val result2 = runTool.handler(mockk(relaxed = true), request)
+            assertThat(result2.isError ?: false).isTrue()
             assertThat(result2.content).isNotEmpty()
             assertThat(result2.content.first().toString()).contains("already 
running")
 
@@ -263,10 +268,10 @@ class StressTestManagerTest {
             val manager = StressTestManager()
             val stopTool = manager.getStopTool()
 
-            val request = CallToolRequest(name = "stop", arguments = 
EmptyJsonObject)
-            val result = stopTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "stop", 
arguments = EmptyJsonObject))
+            val result = stopTool.handler(mockk(relaxed = true), request)
 
-            assertThat(result.isError).isTrue()
+            assertThat(result.isError ?: false).isTrue()
             assertThat(result.content).isNotEmpty()
             assertThat(result.content.first().toString()).contains("No stress 
test is currently running")
         }
@@ -285,17 +290,17 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("iterations", JsonPrimitive(1))
                 }
-            val runRequest = CallToolRequest(name = "run", arguments = runArgs)
-            runTool.handler(runRequest)
+            val runRequest = CallToolRequest(CallToolRequestParams(name = 
"run", arguments = runArgs))
+            runTool.handler(mockk(relaxed = true), runRequest)
 
             // Verify test is running
             assertThat(manager.isRunning()).isTrue()
 
             // Stop the test
-            val stopRequest = CallToolRequest(name = "stop", arguments = 
EmptyJsonObject)
-            val result = stopTool.handler(stopRequest)
+            val stopRequest = CallToolRequest(CallToolRequestParams(name = 
"stop", arguments = EmptyJsonObject))
+            val result = stopTool.handler(mockk(relaxed = true), stopRequest)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
             assertThat(result.content.first().toString()).contains("stopped 
successfully")
 
@@ -310,10 +315,10 @@ class StressTestManagerTest {
             val manager = StressTestManager()
             val statusTool = manager.getStatusTool()
 
-            val request = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
         }
 
@@ -332,18 +337,18 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("iterations", JsonPrimitive(1))
                 }
-            val runRequest = CallToolRequest(name = "run", arguments = runArgs)
-            runTool.handler(runRequest)
+            val runRequest = CallToolRequest(CallToolRequestParams(name = 
"run", arguments = runArgs))
+            runTool.handler(mockk(relaxed = true), runRequest)
 
             // Stop the test
-            val stopRequest = CallToolRequest(name = "stop", arguments = 
EmptyJsonObject)
-            stopTool.handler(stopRequest)
+            val stopRequest = CallToolRequest(CallToolRequestParams(name = 
"stop", arguments = EmptyJsonObject))
+            stopTool.handler(mockk(relaxed = true), stopRequest)
 
             // Check status shows "stopped" message
-            val statusRequest = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(statusRequest)
+            val statusRequest = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), 
statusRequest)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
             val statusText = result.content.first().toString()
             assertThat(statusText).contains("\"status\":\"stopped\"")
@@ -366,11 +371,11 @@ class StressTestManagerTest {
                     put("duration", JsonPrimitive(10))
                     put("threads", JsonPrimitive(1))
                 }
-            val firstRunRequest = CallToolRequest(name = "run", arguments = 
firstTestArgs)
-            val startResult1 = runTool.handler(firstRunRequest)
+            val firstRunRequest = CallToolRequest(CallToolRequestParams(name = 
"run", arguments = firstTestArgs))
+            val startResult1 = runTool.handler(mockk(relaxed = true), 
firstRunRequest)
 
             // Verify first test started successfully
-            assertThat(startResult1.isError).isFalse()
+            assertThat(startResult1.isError ?: false).isFalse()
             
assertThat(startResult1.content.first().toString()).contains("started 
successfully")
             assertThat(manager.isRunning()).isTrue()
             assertThat(manager.getStatus()).isEqualTo("running")
@@ -384,8 +389,8 @@ class StressTestManagerTest {
             }
 
             // 2. STOP FIRST TEST: Stop the running test (or verify it stopped 
on its own)
-            val stopRequest = CallToolRequest(name = "stop", arguments = 
EmptyJsonObject)
-            val stopResult = stopTool.handler(stopRequest)
+            val stopRequest = CallToolRequest(CallToolRequestParams(name = 
"stop", arguments = EmptyJsonObject))
+            val stopResult = stopTool.handler(mockk(relaxed = true), 
stopRequest)
 
             // Verify stop was called and manager is no longer running
             // Note: The test might have already failed and stopped on its 
own, which is fine
@@ -400,11 +405,11 @@ class StressTestManagerTest {
                     put("duration", JsonPrimitive(2))
                     put("threads", JsonPrimitive(1))
                 }
-            val secondRunRequest = CallToolRequest(name = "run", arguments = 
secondTestArgs)
-            val startResult2 = runTool.handler(secondRunRequest)
+            val secondRunRequest = CallToolRequest(CallToolRequestParams(name 
= "run", arguments = secondTestArgs))
+            val startResult2 = runTool.handler(mockk(relaxed = true), 
secondRunRequest)
 
             // Verify second test started successfully
-            assertThat(startResult2.isError).isFalse()
+            assertThat(startResult2.isError ?: false).isFalse()
             
assertThat(startResult2.content.first().toString()).contains("started 
successfully")
             assertThat(manager.isRunning()).isTrue()
             assertThat(manager.getStatus()).isEqualTo("running")
@@ -413,19 +418,22 @@ class StressTestManagerTest {
             assertThat(secondTestStartTime).isNotNull()
 
             // 4. VERIFY STATUS DURING RUN: Check status while second test is 
running
-            val statusRequest = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val statusResult = statusTool.handler(statusRequest)
+            val statusRequest = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val statusResult = statusTool.handler(mockk(relaxed = true), 
statusRequest)
 
-            assertThat(statusResult.isError).isFalse()
+            assertThat(statusResult.isError ?: false).isFalse()
             val statusText = statusResult.content.first().toString()
             assertThat(statusText).contains("\"status\":\"running\"")
             assertThat(statusText).contains("\"is_running\":true")
             assertThat(statusText).contains("currently running")
 
             // 5. LET SECOND TEST COMPLETE: Wait for test to finish naturally
-            // The test will fail to connect but that's expected - it should 
still complete the lifecycle
+            // The test will fail to connect or run for 2s duration - wait for 
completion
             withContext(Dispatchers.IO) {
-                Thread.sleep(3000)
+                for (i in 1..50) {
+                    if (!manager.isRunning()) break
+                    Thread.sleep(100)
+                }
             }
 
             // Verify test completed or stopped (either is acceptable since 
connection will fail)
@@ -435,10 +443,10 @@ class StressTestManagerTest {
             assertThat(finalStatus).matches("^(completed|stopped|failed:.*)$")
 
             // 6. VERIFY POST-COMPLETION STATUS
-            val finalStatusRequest = CallToolRequest(name = "status", 
arguments = EmptyJsonObject)
-            val finalStatusResult = statusTool.handler(finalStatusRequest)
+            val finalStatusRequest = 
CallToolRequest(CallToolRequestParams(name = "status", arguments = 
EmptyJsonObject))
+            val finalStatusResult = statusTool.handler(mockk(relaxed = true), 
finalStatusRequest)
 
-            assertThat(finalStatusResult.isError).isFalse()
+            assertThat(finalStatusResult.isError ?: false).isFalse()
             val finalStatusText = finalStatusResult.content.first().toString()
             assertThat(finalStatusText).contains("\"is_running\":false")
             assertThat(finalStatusText.contains("Last stress test") || 
finalStatusText.contains("Status:")).isTrue()
@@ -462,14 +470,14 @@ class StressTestManagerTest {
                     put("duration", JsonPrimitive(10))
                     put("threads", JsonPrimitive(3))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            runTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            runTool.handler(mockk(relaxed = true), request)
 
             // Check status immediately while still running (before connection 
fails)
-            val statusRequest = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(statusRequest)
+            val statusRequest = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), 
statusRequest)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             val statusText = result.content.first().toString()
 
             // Verify running state
@@ -502,10 +510,10 @@ class StressTestManagerTest {
             val manager = StressTestManager()
             val statusTool = manager.getStatusTool()
 
-            val request = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             val statusText = result.content.first().toString()
 
             assertThat(statusText).contains("\"status\":\"idle\"")
@@ -522,10 +530,10 @@ class StressTestManagerTest {
             manager.setStatus("completed")
 
             val statusTool = manager.getStatusTool()
-            val request = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             val statusText = result.content.first().toString()
 
             assertThat(statusText).contains("\"status\":\"completed\"")
@@ -542,10 +550,10 @@ class StressTestManagerTest {
             manager.setStatus("failed: Connection refused")
 
             val statusTool = manager.getStatusTool()
-            val request = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             val statusText = result.content.first().toString()
 
             assertThat(statusText).contains("\"status\":\"failed: Connection 
refused\"")
@@ -568,8 +576,8 @@ class StressTestManagerTest {
                     put("host", JsonPrimitive("127.0.0.1"))
                     put("duration", JsonPrimitive(1))
                 }
-            val request = CallToolRequest(name = "run", arguments = arguments)
-            runTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "run", 
arguments = arguments))
+            runTool.handler(mockk(relaxed = true), request)
 
             val startTime = manager.getStartTime()
 
@@ -578,10 +586,10 @@ class StressTestManagerTest {
             manager.setStatus("stopped")
 
             // Get status after stopping
-            val statusRequest = CallToolRequest(name = "status", arguments = 
EmptyJsonObject)
-            val result = statusTool.handler(statusRequest)
+            val statusRequest = CallToolRequest(CallToolRequestParams(name = 
"status", arguments = EmptyJsonObject))
+            val result = statusTool.handler(mockk(relaxed = true), 
statusRequest)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             val statusText = result.content.first().toString()
 
             assertThat(statusText).contains("\"is_running\":false")
diff --git 
a/src/test/kotlin/org/apache/cassandra/easystress/server/tools/FieldsToolTest.kt
 
b/src/test/kotlin/org/apache/cassandra/easystress/server/tools/FieldsToolTest.kt
index 9d8fb41..314a0a4 100644
--- 
a/src/test/kotlin/org/apache/cassandra/easystress/server/tools/FieldsToolTest.kt
+++ 
b/src/test/kotlin/org/apache/cassandra/easystress/server/tools/FieldsToolTest.kt
@@ -17,10 +17,12 @@
  */
 package org.apache.cassandra.easystress.server.tools
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.TextContent
+import io.mockk.mockk
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
 import kotlinx.coroutines.test.runTest
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.jsonArray
@@ -35,10 +37,10 @@ class FieldsToolTest {
     @Test
     fun `should list available field generators`() =
         runTest {
-            val request = CallToolRequest(name = "fields", arguments = 
EmptyJsonObject)
-            val result = FieldsTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"fields", arguments = EmptyJsonObject))
+            val result = FieldsTool.handler(mockk(relaxed = true), request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
@@ -59,8 +61,8 @@ class FieldsToolTest {
     @Test
     fun `should include name and description for each generator`() =
         runTest {
-            val request = CallToolRequest(name = "fields", arguments = 
EmptyJsonObject)
-            val result = FieldsTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"fields", arguments = EmptyJsonObject))
+            val result = FieldsTool.handler(mockk(relaxed = true), request)
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
             val generators = jsonResponse["generators"]?.jsonArray
@@ -87,10 +89,10 @@ class FieldsToolTest {
     @Test
     fun `should return consistent results across multiple calls`() =
         runTest {
-            val request = CallToolRequest(name = "fields", arguments = 
EmptyJsonObject)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"fields", arguments = EmptyJsonObject))
 
-            val result1 = FieldsTool.handler(request)
-            val result2 = FieldsTool.handler(request)
+            val result1 = FieldsTool.handler(mockk(relaxed = true), request)
+            val result2 = FieldsTool.handler(mockk(relaxed = true), request)
 
             val response1 = result1.textContent()
             val response2 = result2.textContent()
@@ -102,8 +104,8 @@ class FieldsToolTest {
     @Test
     fun `should include common field generators`() =
         runTest {
-            val request = CallToolRequest(name = "fields", arguments = 
EmptyJsonObject)
-            val result = FieldsTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"fields", arguments = EmptyJsonObject))
+            val result = FieldsTool.handler(mockk(relaxed = true), request)
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
             val generators = jsonResponse["generators"]?.jsonArray
diff --git 
a/src/test/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsToolTest.kt
 
b/src/test/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsToolTest.kt
index 969b3a4..c39c73c 100644
--- 
a/src/test/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsToolTest.kt
+++ 
b/src/test/kotlin/org/apache/cassandra/easystress/server/tools/ListWorkloadsToolTest.kt
@@ -17,10 +17,12 @@
  */
 package org.apache.cassandra.easystress.server.tools
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.TextContent
+import io.mockk.mockk
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
 import kotlinx.coroutines.test.runTest
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.jsonArray
@@ -35,10 +37,10 @@ class ListWorkloadsToolTest {
     @Test
     fun `should list available workloads`() =
         runTest {
-            val request = CallToolRequest(name = "list_workloads", arguments = 
EmptyJsonObject)
-            val result = ListWorkloadsTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"list_workloads", arguments = EmptyJsonObject))
+            val result = ListWorkloadsTool.handler(mockk(relaxed = true), 
request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
@@ -63,8 +65,8 @@ class ListWorkloadsToolTest {
     @Test
     fun `should include known workloads`() =
         runTest {
-            val request = CallToolRequest(name = "list_workloads", arguments = 
EmptyJsonObject)
-            val result = ListWorkloadsTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"list_workloads", arguments = EmptyJsonObject))
+            val result = ListWorkloadsTool.handler(mockk(relaxed = true), 
request)
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
             val workloads = jsonResponse["workloads"]?.jsonArray
@@ -82,10 +84,10 @@ class ListWorkloadsToolTest {
     @Test
     fun `should return consistent results across multiple calls`() =
         runTest {
-            val request = CallToolRequest(name = "list_workloads", arguments = 
EmptyJsonObject)
+            val request = CallToolRequest(CallToolRequestParams(name = 
"list_workloads", arguments = EmptyJsonObject))
 
-            val result1 = ListWorkloadsTool.handler(request)
-            val result2 = ListWorkloadsTool.handler(request)
+            val result1 = ListWorkloadsTool.handler(mockk(relaxed = true), 
request)
+            val result2 = ListWorkloadsTool.handler(mockk(relaxed = true), 
request)
 
             val response1 = result1.textContent()
             val response2 = result2.textContent()
diff --git 
a/src/test/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoToolTest.kt
 
b/src/test/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoToolTest.kt
index 9162d50..0502b36 100644
--- 
a/src/test/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoToolTest.kt
+++ 
b/src/test/kotlin/org/apache/cassandra/easystress/server/tools/WorkloadInfoToolTest.kt
@@ -17,9 +17,11 @@
  */
 package org.apache.cassandra.easystress.server.tools
 
-import io.modelcontextprotocol.kotlin.sdk.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.TextContent
+import io.mockk.mockk
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
+import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
+import io.modelcontextprotocol.kotlin.sdk.types.TextContent
 import kotlinx.coroutines.test.runTest
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonPrimitive
@@ -32,7 +34,7 @@ import org.assertj.core.api.Assertions.assertThat
 import org.junit.jupiter.api.Test
 
 class WorkloadInfoToolTest {
-    private fun CallToolResult.textContent(): String = (this.content.first() 
as TextContent).text!!
+    private fun CallToolResult.textContent(): String = (this.content.first() 
as TextContent).text
 
     @Test
     fun `should require workload parameter`() {
@@ -47,10 +49,10 @@ class WorkloadInfoToolTest {
                 buildJsonObject {
                     put("workload", JsonPrimitive("nonexistent_workload_xyz"))
                 }
-            val request = CallToolRequest(name = "info", arguments = arguments)
-            val result = WorkloadInfoTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "info", 
arguments = arguments))
+            val result = WorkloadInfoTool.handler(mockk(relaxed = true), 
request)
 
-            assertThat(result.isError).isTrue()
+            assertThat(result.isError ?: false).isTrue()
             assertThat(result.textContent()).contains("not found")
         }
 
@@ -65,10 +67,10 @@ class WorkloadInfoToolTest {
                 buildJsonObject {
                     put("workload", JsonPrimitive(firstWorkload))
                 }
-            val request = CallToolRequest(name = "info", arguments = arguments)
-            val result = WorkloadInfoTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "info", 
arguments = arguments))
+            val result = WorkloadInfoTool.handler(mockk(relaxed = true), 
request)
 
-            assertThat(result.isError).isFalse()
+            assertThat(result.isError ?: false).isFalse()
             assertThat(result.content).isNotEmpty()
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
@@ -102,8 +104,8 @@ class WorkloadInfoToolTest {
                 buildJsonObject {
                     put("workload", JsonPrimitive(firstWorkload))
                 }
-            val request = CallToolRequest(name = "info", arguments = arguments)
-            val result = WorkloadInfoTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "info", 
arguments = arguments))
+            val result = WorkloadInfoTool.handler(mockk(relaxed = true), 
request)
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
             val schema = jsonResponse["schema"]?.jsonArray
@@ -127,8 +129,8 @@ class WorkloadInfoToolTest {
                 buildJsonObject {
                     put("workload", JsonPrimitive(firstWorkload))
                 }
-            val request = CallToolRequest(name = "info", arguments = arguments)
-            val result = WorkloadInfoTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "info", 
arguments = arguments))
+            val result = WorkloadInfoTool.handler(mockk(relaxed = true), 
request)
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
 
@@ -147,8 +149,8 @@ class WorkloadInfoToolTest {
                 buildJsonObject {
                     put("workload", JsonPrimitive(firstWorkload))
                 }
-            val request = CallToolRequest(name = "info", arguments = arguments)
-            val result = WorkloadInfoTool.handler(request)
+            val request = CallToolRequest(CallToolRequestParams(name = "info", 
arguments = arguments))
+            val result = WorkloadInfoTool.handler(mockk(relaxed = true), 
request)
 
             val jsonResponse = 
Json.parseToJsonElement(result.textContent()).jsonObject
             val parameters = jsonResponse["parameters"]?.jsonArray


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to