Skip to content

Kotlin SDK

Model Context Protocol (MCP) 的 Kotlin SDK 提供了构建 MCP 服务器和客户端的现代 Kotlin 解决方案,支持协程、类型安全和函数式编程特性。

安装

Gradle (Kotlin DSL)

kotlin
dependencies {
    implementation("io.modelcontextprotocol:mcp-sdk-kotlin:1.0.0")
    
    // 协程支持
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
    
    // JSON 序列化
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}

Gradle (Groovy)

groovy
dependencies {
    implementation 'io.modelcontextprotocol:mcp-sdk-kotlin:1.0.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0'
}

Maven

xml
<dependency>
    <groupId>io.modelcontextprotocol</groupId>
    <artifactId>mcp-sdk-kotlin</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-core</artifactId>
    <version>1.7.3</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-serialization-json</artifactId>
    <version>1.6.0</version>
</dependency>

快速开始

创建 MCP 服务器

kotlin
import io.mcp.sdk.server.*
import io.mcp.sdk.tools.*
import io.mcp.sdk.schema.*
import kotlinx.coroutines.*
import kotlinx.serialization.json.*

suspend fun main() {
    // 创建服务器
    val server = McpServer {
        name = "my-kotlin-server"
        version = "1.0.0"
        description = "Kotlin MCP 服务器示例"
    }
    
    // 注册工具
    server.registerTool<EchoTool>()
    
    // 启动服务器
    server.start()
}

@Serializable
data class EchoRequest(val message: String)

@Serializable
data class EchoResponse(val echo: String)

class EchoTool : Tool<EchoRequest, EchoResponse> {
    override val name = "echo"
    override val description = "回显输入的消息"
    
    override suspend fun call(request: EchoRequest): ToolResult<EchoResponse> {
        return ToolResult.success(EchoResponse(echo = request.message))
    }
}

创建 MCP 客户端

kotlin
import io.mcp.sdk.client.*
import io.mcp.sdk.transport.*
import kotlinx.coroutines.*

suspend fun main() {
    // 创建传输层
    val transport = StdioTransport()
    
    // 创建客户端
    val client = McpClient {
        transport = transport
        connectTimeout = 10.seconds
        requestTimeout = 30.seconds
    }
    
    try {
        // 连接到服务器
        client.connect()
        
        // 列出可用工具
        val tools = client.listTools()
        println("可用工具: ${tools.joinToString { it.name }}")
        
        // 调用工具
        val result = client.callTool<EchoResponse>("echo") {
            "message" to "Hello from Kotlin!"
        }
        
        println("工具调用结果: ${result.echo}")
    } finally {
        client.close()
    }
}

核心功能

服务器功能

工具注册

kotlin
// 计算器工具
@Serializable
data class CalculatorRequest(
    val operation: Operation,
    val a: Double,
    val b: Double
)

@Serializable
enum class Operation {
    ADD, SUBTRACT, MULTIPLY, DIVIDE
}

@Serializable
data class CalculatorResponse(val result: Double)

class CalculatorTool : Tool<CalculatorRequest, CalculatorResponse> {
    override val name = "calculator"
    override val description = "执行基本数学运算"
    
    override suspend fun call(request: CalculatorRequest): ToolResult<CalculatorResponse> {
        val result = when (request.operation) {
            Operation.ADD -> request.a + request.b
            Operation.SUBTRACT -> request.a - request.b
            Operation.MULTIPLY -> request.a * request.b
            Operation.DIVIDE -> {
                if (request.b == 0.0) {
                    return ToolResult.error("除数不能为零")
                }
                request.a / request.b
            }
        }
        
        return ToolResult.success(CalculatorResponse(result))
    }
}

// 注册工具
server.registerTool<CalculatorTool>()

// 或者注册实例
server.registerTool(CalculatorTool())

资源管理

kotlin
import io.mcp.sdk.resources.*
import java.io.File
import java.nio.file.Path
import kotlin.io.path.*

class FileResourceProvider(private val basePath: Path) : ResourceProvider {
    
    override suspend fun getResource(uri: ResourceUri): Resource {
        val filePath = basePath / uri.path.removePrefix("/")
        
        if (!filePath.exists()) {
            throw ResourceNotFoundException("资源不存在: $uri")
        }
        
        val content = filePath.readText()
        
        return Resource(
            uri = uri,
            mimeType = "text/plain",
            content = content
        )
    }
    
    override suspend fun listResources(): List<ResourceUri> {
        return basePath.walk()
            .filter { it.isRegularFile() }
            .map { file ->
                val relativePath = basePath.relativize(file)
                ResourceUri("file:///$relativePath")
            }
            .toList()
    }
}

// 注册资源提供者
server.registerResourceProvider(FileResourceProvider(Path.of("/path/to/files")))

提示模板

kotlin
import io.mcp.sdk.prompts.*

@Serializable
data class CodeReviewRequest(
    val code: String,
    val language: String = "unknown"
)

class CodeReviewPromptProvider : PromptProvider {
    
    override suspend fun getPrompt(name: String, arguments: JsonObject): Prompt {
        return when (name) {
            "code-review" -> createCodeReviewPrompt(arguments)
            else -> throw PromptNotFoundException("未知提示: $name")
        }
    }
    
    override suspend fun listPrompts(): List<String> {
        return listOf("code-review")
    }
    
    private suspend fun createCodeReviewPrompt(arguments: JsonObject): Prompt {
        val code = arguments["code"]?.jsonPrimitive?.content
            ?: throw IllegalArgumentException("缺少 code 参数")
        
        val language = arguments["language"]?.jsonPrimitive?.content ?: "unknown"
        
        val content = """
            请审查以下 $language 代码并提供改进建议:
            
            ```$language
            $code
            ```
        """.trimIndent()
        
        return Prompt(
            name = "code-review",
            description = "代码审查提示",
            content = content
        )
    }
}

// 注册提示提供者
server.registerPromptProvider(CodeReviewPromptProvider())

客户端功能

连接管理

kotlin
// 自动重连客户端
val client = McpClient {
    transport = transport
    autoReconnect = true
    maxReconnectAttempts = 5
    reconnectDelay = 2.seconds
}

// 连接状态监听
client.onConnected { 
    println("已连接到服务器") 
}

client.onDisconnected { 
    println("与服务器断开连接") 
}

client.onError { exception ->
    println("连接错误: ${exception.message}")
}

并发操作

kotlin
// 并发调用多个工具
val results = coroutineScope {
    listOf(
        async { client.callTool<String>("tool1", args1) },
        async { client.callTool<String>("tool2", args2) },
        async { client.callTool<String>("tool3", args3) }
    ).awaitAll()
}

results.forEachIndexed { index, result ->
    println("结果 ${index + 1}: $result")
}

流式操作

kotlin
// 流式工具调用
client.callToolStream<String>("streaming-tool", args)
    .collect { chunk ->
        print(chunk)
    }

传输协议

Stdio 传输

kotlin
val transport = StdioTransport {
    command = listOf("python", "server.py")
    workingDirectory = Path.of("/path/to/server")
    environment = mapOf("ENV_VAR" to "value")
}

WebSocket 传输

kotlin
val transport = WebSocketTransport {
    uri = "ws://localhost:8080/mcp"
    headers = mapOf("Authorization" to "Bearer token")
    connectTimeout = 10.seconds
}

HTTP SSE 传输

kotlin
val transport = SseTransport {
    uri = "http://localhost:8080/mcp"
    headers = mapOf("Authorization" to "Bearer token")
    readTimeout = 30.seconds
}

高级特性

依赖注入支持 (Koin)

kotlin
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
import org.koin.core.context.startKoin

val mcpModule = module {
    // 注册 MCP 服务器
    single {
        McpServer {
            name = "my-server"
            version = "1.0.0"
        }
    }
    
    // 注册工具
    singleOf(::DatabaseTool)
    singleOf(::EmailTool)
    
    // 注册资源提供者
    singleOf(::FileResourceProvider)
    
    // 注册其他服务
    singleOf(::EmailService)
    singleOf(::DatabaseService)
}

suspend fun main() {
    startKoin {
        modules(mcpModule)
    }
    
    val server = get<McpServer>()
    server.start()
}

// 带依赖注入的工具
class DatabaseTool(
    private val databaseService: DatabaseService
) : Tool<DatabaseQuery, DatabaseResult> {
    
    override val name = "database-query"
    override val description = "执行数据库查询"
    
    override suspend fun call(request: DatabaseQuery): ToolResult<DatabaseResult> {
        return try {
            val result = databaseService.executeQuery(request.sql)
            ToolResult.success(DatabaseResult(result))
        } catch (e: Exception) {
            ToolResult.error("查询执行失败: ${e.message}")
        }
    }
}

中间件支持

kotlin
interface Middleware {
    suspend fun handle(request: Request, next: suspend (Request) -> Response): Response
}

class LoggingMiddleware : Middleware {
    override suspend fun handle(request: Request, next: suspend (Request) -> Response): Response {
        println("处理请求: ${request.method}")
        val startTime = System.currentTimeMillis()
        
        return try {
            val response = next(request)
            val duration = System.currentTimeMillis() - startTime
            println("请求完成,耗时: ${duration}ms")
            response
        } catch (e: Exception) {
            val duration = System.currentTimeMillis() - startTime
            println("请求处理失败,耗时: ${duration}ms")
            throw e
        }
    }
}

// 注册中间件
server.use(LoggingMiddleware())
server.use(AuthenticationMiddleware())
server.use(RateLimitingMiddleware())

配置管理

kotlin
import kotlinx.serialization.Serializable

@Serializable
data class McpConfig(
    val server: ServerConfig = ServerConfig(),
    val logging: LoggingConfig = LoggingConfig(),
    val transport: TransportConfig = TransportConfig()
)

@Serializable
data class ServerConfig(
    val name: String = "",
    val version: String = "",
    val description: String = "",
    val maxConnections: Int = 100,
    val requestTimeoutSeconds: Int = 30
)

@Serializable
data class LoggingConfig(
    val level: String = "INFO",
    val file: String? = null
)

@Serializable
data class TransportConfig(
    val type: String = "stdio",
    val bufferSize: Int = 8192
)

// 加载配置
val config = Json.decodeFromString<McpConfig>(
    File("config.json").readText()
)

// 使用配置创建服务器
val server = McpServer {
    name = config.server.name
    version = config.server.version
    description = config.server.description
    maxConnections = config.server.maxConnections
    requestTimeout = config.server.requestTimeoutSeconds.seconds
}

类型安全的工具调用

kotlin
// 定义工具接口
interface WeatherTool {
    suspend fun getCurrentWeather(location: String): WeatherResponse
    suspend fun getForecast(location: String, days: Int): ForecastResponse
}

@Serializable
data class WeatherResponse(
    val temperature: Double,
    val humidity: Int,
    val description: String
)

@Serializable
data class ForecastResponse(
    val location: String,
    val forecasts: List<DailyForecast>
)

@Serializable
data class DailyForecast(
    val date: String,
    val temperature: TemperatureRange,
    val description: String
)

@Serializable
data class TemperatureRange(
    val min: Double,
    val max: Double
)

// 实现工具
class WeatherToolImpl : WeatherTool {
    override suspend fun getCurrentWeather(location: String): WeatherResponse {
        // 实现获取当前天气的逻辑
        return WeatherResponse(
            temperature = 25.0,
            humidity = 60,
            description = "晴朗"
        )
    }
    
    override suspend fun getForecast(location: String, days: Int): ForecastResponse {
        // 实现获取天气预报的逻辑
        return ForecastResponse(
            location = location,
            forecasts = emptyList()
        )
    }
}

// 客户端类型安全调用
val weatherTool = client.createProxy<WeatherTool>()
val weather = weatherTool.getCurrentWeather("北京")
println("当前温度: ${weather.temperature}°C")

测试

单元测试

kotlin
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class EchoToolTest {
    
    @Test
    fun `call with valid message returns echo`() = runTest {
        // Arrange
        val tool = EchoTool()
        val request = EchoRequest("test")
        
        // Act
        val result = tool.call(request)
        
        // Assert
        assertTrue(result.isSuccess)
        assertEquals("test", result.data?.echo)
    }
    
    @Test
    fun `calculator tool performs addition correctly`() = runTest {
        // Arrange
        val tool = CalculatorTool()
        val request = CalculatorRequest(Operation.ADD, 2.0, 3.0)
        
        // Act
        val result = tool.call(request)
        
        // Assert
        assertTrue(result.isSuccess)
        assertEquals(5.0, result.data?.result)
    }
}

集成测试

kotlin
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class McpIntegrationTest {
    
    @Test
    fun `server client integration test`() = runTest {
        // 启动测试服务器
        val server = McpServer {
            name = "test-server"
        }
        server.registerTool<EchoTool>()
        
        val serverJob = launch { server.start() }
        
        try {
            // 创建客户端
            val transport = StdioTransport()
            val client = McpClient { transport = transport }
            
            client.connect()
            
            // 测试工具调用
            val result = client.callTool<EchoResponse>("echo") {
                "message" to "test"
            }
            
            assertEquals("test", result.echo)
        } finally {
            serverJob.cancel()
        }
    }
}

部署

Spring Boot 集成

kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@SpringBootApplication
class McpServerApplication

fun main(args: Array<String>) {
    runApplication<McpServerApplication>(*args)
}

@Configuration
class McpConfiguration {
    
    @Bean
    fun mcpServer(): McpServer {
        return McpServer {
            name = "spring-mcp-server"
            version = "1.0.0"
        }
    }
    
    @Bean
    fun echoTool(): EchoTool = EchoTool()
}

Docker 部署

dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

COPY build/libs/mcp-server.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

Kubernetes 部署

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mcp-server
  template:
    metadata:
      labels:
        app: mcp-server
    spec:
      containers:
      - name: mcp-server
        image: my-mcp-server:latest
        ports:
        - containerPort: 8080
        env:
        - name: MCP_SERVER_NAME
          value: "kubernetes-mcp-server"
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-server-service
spec:
  selector:
    app: mcp-server
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

相关资源

社区支持

🚀 探索模型上下文协议的无限可能 | 连接 AI 与世界的桥梁 | 让智能更智能