Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/gradle-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:

jobs:
build:
runs-on: ubuntu-latest
runs-on: macos-latest
environment: release

permissions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
validate-pr:
runs-on: ubuntu-latest
runs-on: macos-latest
name: Validate PR
steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ out/

### Kotlin ###
.kotlin
yarn.lock

### Eclipse ###
.apt_generated
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# MCP Kotlin SDK

Kotlin implementation of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP), providing both client and server capabilities for integrating with LLM surfaces.
[![Kotlin Multiplatform](https://img.shields.io/badge/Kotlin-Multiplatform-blueviolet?logo=kotlin)](https://kotlinlang.org/docs/multiplatform.html)
[![Platforms](https://img.shields.io/badge/Platforms-JVM%20%7C%20Wasm%2FJS%20%7C%20Native%20(iOS%2FiOS%20Simulator)-blue)](https://kotlinlang.org/docs/multiplatform.html)
[![Maven Central](https://img.shields.io/maven-central/v/io.modelcontextprotocol/kotlin-sdk.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:io.modelcontextprotocol%20a:kotlin-sdk)
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Kotlin Multiplatform implementation of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP),
providing both client and server capabilities for integrating with LLM surfaces across various platforms.

## Overview

The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This Kotlin SDK implements the full MCP specification, making it easy to:
The Model Context Protocol allows applications to provide context for LLMs in a standardized way,
separating the concerns of providing context from the actual LLM interaction.
This SDK implements the MCP specification for Kotlin,
enabling you to build applications that can communicate using MCP on the JVM, WebAssembly and iOS.

- Build MCP clients that can connect to any MCP server
- Create MCP servers that expose resources, prompts and tools
Expand All @@ -13,7 +22,7 @@ The Model Context Protocol allows applications to provide context for LLMs in a

## Samples

- [kotlin-mcp-server](./samples/kotlin-mcp-server): shows how to set up a Kotlin MCP server with different tools and other features.
- [kotlin-mcp-server](./samples/kotlin-mcp-server): demonstrates a multiplatform (JVM, Wasm) MCP server setup with various features and transports.
- [weather-stdio-server](./samples/weather-stdio-server): shows how to build a Kotlin MCP server providing weather forecast and alerts using STDIO transport.
- [kotlin-mcp-client](./samples/kotlin-mcp-client): demonstrates building an interactive Kotlin MCP client that connects to an MCP server via STDIO and integrates with Anthropic’s API.

Expand All @@ -31,7 +40,8 @@ Add the dependency:

```kotlin
dependencies {
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
// Use the badge above for the latest version
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
}
```

Expand Down
83 changes: 1 addition & 82 deletions api/kotlin-sdk.api

Large diffs are not rendered by default.

50 changes: 46 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalWasmDsl::class)

import org.jetbrains.dokka.gradle.engine.parameters.VisibilityModifier
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jreleaser.model.Active

plugins {
Expand All @@ -27,9 +32,11 @@ publishing {
val javadocJar = configureEmptyJavadocArtifact()

publications.withType(MavenPublication::class).all {
if (name.contains("jvm", ignoreCase = true)) {
artifact(javadocJar)
}
pom.configureMavenCentralMetadata()
signPublicationIfKeyPresent()
artifact(javadocJar)
}

repositories {
Expand All @@ -53,10 +60,28 @@ jreleaser {
active.set(Active.ALWAYS)
mavenCentral {
val ossrh by creating {
applyMavenCentralRules = true
active.set(Active.ALWAYS)
url.set("https://central.sonatype.com/api/v1/publisher")
applyMavenCentralRules = false
maxRetries = 240
stagingRepository(layout.buildDirectory.dir("staging-deploy").get().asFile.path)
// workaround: https://github.com/jreleaser/jreleaser/issues/1784
kotlin.targets.forEach { target ->
if (target !is KotlinJvmTarget && target !is KotlinAndroidTarget && target !is KotlinMetadataTarget) {
val klibArtifactId = if (target.platformType == KotlinPlatformType.wasm) {
"${name}-wasm-${target.name.lowercase().substringAfter("wasm")}"
} else {
"${name}-${target.name.lowercase()}"
}
artifactOverride {
artifactId = klibArtifactId
jar = false
verifyPom = false
sourceJar = false
javadocJar = false
}
}
}
}
}
}
Expand Down Expand Up @@ -194,6 +219,24 @@ kotlin {
}
}

iosArm64()
iosX64()
iosSimulatorArm64()

js(IR) {
nodejs {
testTask {
useMocha {
timeout = "30s"
}
}
}
}

wasmJs {
nodejs()
}

explicitApi = ExplicitApiMode.Strict

jvmToolchain(21)
Expand All @@ -217,7 +260,6 @@ kotlin {
implementation(libs.kotlin.test)
implementation(libs.ktor.server.test.host)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.kotlinx.coroutines.debug)
implementation(libs.kotest.assertions.json)
}
}
Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ org.gradle.configuration-cache.parallel=true

org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true

kotlin.daemon.jvmargs=-Xmx2G
5 changes: 2 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ dokka = "2.0.0"

# libraries version
serialization = "1.7.3"
coroutines = "1.9.0"
ktor = "3.0.2"
coroutines = "1.10.2"
ktor = "3.1.2"
mockk = "1.13.13"
logging = "7.0.0"
jreleaser = "1.17.0"
Expand All @@ -28,7 +28,6 @@ ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio", version.ref = "
# Testing
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
kotlinx-coroutines-debug = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-debug", version.ref = "coroutines" }
ktor-server-test-host = { group = "io.ktor", name = "ktor-server-test-host", version.ref = "ktor" }
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
Expand Down
32 changes: 24 additions & 8 deletions samples/kotlin-mcp-server/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# MCP Kotlin Server Sample

A sample implementation of an MCP (Model Communication Protocol) server in Kotlin that demonstrates different server configurations and transport methods.
A sample implementation of an MCP (Model Communication Protocol) server in Kotlin that demonstrates different server
configurations and transport methods for both JVM and WASM targets.

## Features

- Multiple server operation modes:
- Standard I/O server
- SSE (Server-Sent Events) server with plain configuration
- SSE server using Ktor plugin
- Standard I/O server (JVM only)
- SSE (Server-Sent Events) server with plain configuration (JVM, WASM)
- SSE server using Ktor plugin (JVM, WASM)
- Multiplatform support
- Built-in capabilities for:
- Prompts management
- Resources handling
Expand All @@ -17,17 +19,30 @@ A sample implementation of an MCP (Model Communication Protocol) server in Kotli

### Running the Server

To run the server in SSE mode on the port 3001, run:
You can run the server on the JVM or using Kotlin/WASM on Node.js.


#### JVM:

To run the server on the JVM (defaults to SSE mode with Ktor plugin on port 3001):

```bash
./gradlew runJvm
```

#### WASM:

To run the server using Kotlin/WASM on Node.js (defaults to SSE mode with Ktor plugin on port 3001):

```bash
./gradlew run
./gradlew wasmJsNodeDevelopmentRun
```

### Connecting to the Server

For SSE servers (both plain and Ktor plugin versions):
For servers on JVM or WASM:
1. Start the server
2. Use the MCP inspector to connect to `http://localhost:<port>/sse`
2. Use the [MCP inspector](https://modelcontextprotocol.io/docs/tools/inspector) to connect to `http://localhost:<port>/sse`

## Server Capabilities

Expand All @@ -42,3 +57,4 @@ The server is implemented using:
- Kotlin coroutines for asynchronous operations
- SSE for real-time communication
- Standard I/O for command-line interface
- Common Kotlin code shared between JVM and WASM targets
64 changes: 41 additions & 23 deletions samples/kotlin-mcp-server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,35 +1,53 @@
plugins {
kotlin("jvm") version "2.1.0"
kotlin("plugin.serialization") version "2.1.0"
application
}
@file:OptIn(ExperimentalWasmDsl::class, ExperimentalKotlinGradlePluginApi::class)

application {
mainClass.set("MainKt")
}
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl

plugins {
kotlin("multiplatform") version "2.1.20"
kotlin("plugin.serialization") version "2.1.20"
}

group = "org.example"
version = "0.1.0"

dependencies {
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
implementation("org.slf4j:slf4j-nop:2.0.9")
repositories {
mavenCentral()
}

tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(21)
}
val jvmMainClass = "Main_jvmKt"

tasks.jar {
manifest {
attributes["Main-Class"] = "MainKt"
kotlin {
jvmToolchain(17)
jvm {
binaries {
executable {
mainClass.set(jvmMainClass)
}
}
val jvmJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
doFirst {
manifest {
attributes["Main-Class"] = jvmMainClass
}

from(configurations["jvmRuntimeClasspath"].map { if (it.isDirectory) it else zipTree(it) })
}
}
}
wasmJs {
nodejs()
binaries.executable()
}

duplicatesStrategy = DuplicatesStrategy.EXCLUDE

from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
sourceSets {
commonMain.dependencies {
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
}
jvmMain.dependencies {
implementation("org.slf4j:slf4j-nop:2.0.9")
}
wasmJsMain.dependencies {}
}
}
2 changes: 2 additions & 0 deletions samples/kotlin-mcp-server/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
kotlin.code.style=official

kotlin.daemon.jvmargs=-Xmx2G
8 changes: 0 additions & 8 deletions samples/kotlin-mcp-server/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,3 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}
rootProject.name = "kotlin-mcp-server"

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/kotlin-mcp-sdk/sdk")
}
}
Loading
Loading