Skip to content

Commit 0cff2ca

Browse files
authored
add ios and wasm targets (#81)
* add wasm and ios targets * add yarn.lock file to gitignore * clean up code * fix typo in method name * add wasm server example * update jrelease publishing for klibs * update gradle-publish workflow to use macos runner * update README to reflect multiplatform support and clarify server setup for JVM and WASM * remove the old main file from the kotlin-mcp-server example * update api dump * Refactor `build.gradle.kts` * clean up imports and remove unnecessary charset specification in tests * add JS target configuration and update API dump
1 parent 7ce665b commit 0cff2ca

File tree

51 files changed

+705
-478
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+705
-478
lines changed

.github/workflows/gradle-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ on:
1313

1414
jobs:
1515
build:
16-
runs-on: ubuntu-latest
16+
runs-on: macos-latest
1717
environment: release
1818

1919
permissions:

.github/workflows/validate-pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
jobs:
99
validate-pr:
10-
runs-on: ubuntu-latest
10+
runs-on: macos-latest
1111
name: Validate PR
1212
steps:
1313
- uses: actions/checkout@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ out/
2020

2121
### Kotlin ###
2222
.kotlin
23+
yarn.lock
2324

2425
### Eclipse ###
2526
.apt_generated

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
# MCP Kotlin SDK
22

3-
Kotlin implementation of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP), providing both client and server capabilities for integrating with LLM surfaces.
3+
[![Kotlin Multiplatform](https://img.shields.io/badge/Kotlin-Multiplatform-blueviolet?logo=kotlin)](https://kotlinlang.org/docs/multiplatform.html)
4+
[![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)
5+
[![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)
6+
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7+
8+
Kotlin Multiplatform implementation of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP),
9+
providing both client and server capabilities for integrating with LLM surfaces across various platforms.
410

511
## Overview
612

7-
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:
13+
The Model Context Protocol allows applications to provide context for LLMs in a standardized way,
14+
separating the concerns of providing context from the actual LLM interaction.
15+
This SDK implements the MCP specification for Kotlin,
16+
enabling you to build applications that can communicate using MCP on the JVM, WebAssembly and iOS.
817

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

1423
## Samples
1524

16-
- [kotlin-mcp-server](./samples/kotlin-mcp-server): shows how to set up a Kotlin MCP server with different tools and other features.
25+
- [kotlin-mcp-server](./samples/kotlin-mcp-server): demonstrates a multiplatform (JVM, Wasm) MCP server setup with various features and transports.
1726
- [weather-stdio-server](./samples/weather-stdio-server): shows how to build a Kotlin MCP server providing weather forecast and alerts using STDIO transport.
1827
- [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.
1928

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

3241
```kotlin
3342
dependencies {
34-
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
43+
// Use the badge above for the latest version
44+
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
3545
}
3646
```
3747

api/kotlin-sdk.api

Lines changed: 1 addition & 82 deletions
Large diffs are not rendered by default.

build.gradle.kts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
1+
@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalWasmDsl::class)
22

33
import org.jetbrains.dokka.gradle.engine.parameters.VisibilityModifier
44
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
5+
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
56
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
67
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
8+
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
9+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
10+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
11+
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
712
import org.jreleaser.model.Active
813

914
plugins {
@@ -27,9 +32,11 @@ publishing {
2732
val javadocJar = configureEmptyJavadocArtifact()
2833

2934
publications.withType(MavenPublication::class).all {
35+
if (name.contains("jvm", ignoreCase = true)) {
36+
artifact(javadocJar)
37+
}
3038
pom.configureMavenCentralMetadata()
3139
signPublicationIfKeyPresent()
32-
artifact(javadocJar)
3340
}
3441

3542
repositories {
@@ -53,10 +60,28 @@ jreleaser {
5360
active.set(Active.ALWAYS)
5461
mavenCentral {
5562
val ossrh by creating {
56-
applyMavenCentralRules = true
5763
active.set(Active.ALWAYS)
5864
url.set("https://central.sonatype.com/api/v1/publisher")
65+
applyMavenCentralRules = false
66+
maxRetries = 240
5967
stagingRepository(layout.buildDirectory.dir("staging-deploy").get().asFile.path)
68+
// workaround: https://github.com/jreleaser/jreleaser/issues/1784
69+
kotlin.targets.forEach { target ->
70+
if (target !is KotlinJvmTarget && target !is KotlinAndroidTarget && target !is KotlinMetadataTarget) {
71+
val klibArtifactId = if (target.platformType == KotlinPlatformType.wasm) {
72+
"${name}-wasm-${target.name.lowercase().substringAfter("wasm")}"
73+
} else {
74+
"${name}-${target.name.lowercase()}"
75+
}
76+
artifactOverride {
77+
artifactId = klibArtifactId
78+
jar = false
79+
verifyPom = false
80+
sourceJar = false
81+
javadocJar = false
82+
}
83+
}
84+
}
6085
}
6186
}
6287
}
@@ -194,6 +219,24 @@ kotlin {
194219
}
195220
}
196221

222+
iosArm64()
223+
iosX64()
224+
iosSimulatorArm64()
225+
226+
js(IR) {
227+
nodejs {
228+
testTask {
229+
useMocha {
230+
timeout = "30s"
231+
}
232+
}
233+
}
234+
}
235+
236+
wasmJs {
237+
nodejs()
238+
}
239+
197240
explicitApi = ExplicitApiMode.Strict
198241

199242
jvmToolchain(21)
@@ -217,7 +260,6 @@ kotlin {
217260
implementation(libs.kotlin.test)
218261
implementation(libs.ktor.server.test.host)
219262
implementation(libs.kotlinx.coroutines.test)
220-
implementation(libs.kotlinx.coroutines.debug)
221263
implementation(libs.kotest.assertions.json)
222264
}
223265
}

gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ org.gradle.configuration-cache.parallel=true
55

66
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
77
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
8+
9+
kotlin.daemon.jvmargs=-Xmx2G

gradle/libs.versions.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ dokka = "2.0.0"
55

66
# libraries version
77
serialization = "1.7.3"
8-
coroutines = "1.9.0"
9-
ktor = "3.0.2"
8+
coroutines = "1.10.2"
9+
ktor = "3.1.2"
1010
mockk = "1.13.13"
1111
logging = "7.0.0"
1212
jreleaser = "1.17.0"
@@ -28,7 +28,6 @@ ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio", version.ref = "
2828
# Testing
2929
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
3030
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
31-
kotlinx-coroutines-debug = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-debug", version.ref = "coroutines" }
3231
ktor-server-test-host = { group = "io.ktor", name = "ktor-server-test-host", version.ref = "ktor" }
3332
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
3433
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }

samples/kotlin-mcp-server/README.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# MCP Kotlin Server Sample
22

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

56
## Features
67

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

1820
### Running the Server
1921

20-
To run the server in SSE mode on the port 3001, run:
22+
You can run the server on the JVM or using Kotlin/WASM on Node.js.
23+
24+
25+
#### JVM:
26+
27+
To run the server on the JVM (defaults to SSE mode with Ktor plugin on port 3001):
28+
29+
```bash
30+
./gradlew runJvm
31+
```
32+
33+
#### WASM:
34+
35+
To run the server using Kotlin/WASM on Node.js (defaults to SSE mode with Ktor plugin on port 3001):
2136

2237
```bash
23-
./gradlew run
38+
./gradlew wasmJsNodeDevelopmentRun
2439
```
2540

2641
### Connecting to the Server
2742

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

3247
## Server Capabilities
3348

@@ -42,3 +57,4 @@ The server is implemented using:
4257
- Kotlin coroutines for asynchronous operations
4358
- SSE for real-time communication
4459
- Standard I/O for command-line interface
60+
- Common Kotlin code shared between JVM and WASM targets
Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,53 @@
1-
plugins {
2-
kotlin("jvm") version "2.1.0"
3-
kotlin("plugin.serialization") version "2.1.0"
4-
application
5-
}
1+
@file:OptIn(ExperimentalWasmDsl::class, ExperimentalKotlinGradlePluginApi::class)
62

7-
application {
8-
mainClass.set("MainKt")
9-
}
3+
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
4+
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
105

6+
plugins {
7+
kotlin("multiplatform") version "2.1.20"
8+
kotlin("plugin.serialization") version "2.1.20"
9+
}
1110

1211
group = "org.example"
1312
version = "0.1.0"
1413

15-
dependencies {
16-
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
17-
implementation("org.slf4j:slf4j-nop:2.0.9")
14+
repositories {
15+
mavenCentral()
1816
}
1917

20-
tasks.test {
21-
useJUnitPlatform()
22-
}
23-
kotlin {
24-
jvmToolchain(21)
25-
}
18+
val jvmMainClass = "Main_jvmKt"
2619

27-
tasks.jar {
28-
manifest {
29-
attributes["Main-Class"] = "MainKt"
20+
kotlin {
21+
jvmToolchain(17)
22+
jvm {
23+
binaries {
24+
executable {
25+
mainClass.set(jvmMainClass)
26+
}
27+
}
28+
val jvmJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
29+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
30+
doFirst {
31+
manifest {
32+
attributes["Main-Class"] = jvmMainClass
33+
}
34+
35+
from(configurations["jvmRuntimeClasspath"].map { if (it.isDirectory) it else zipTree(it) })
36+
}
37+
}
38+
}
39+
wasmJs {
40+
nodejs()
41+
binaries.executable()
3042
}
3143

32-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
33-
34-
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
44+
sourceSets {
45+
commonMain.dependencies {
46+
implementation("io.modelcontextprotocol:kotlin-sdk:0.5.0")
47+
}
48+
jvmMain.dependencies {
49+
implementation("org.slf4j:slf4j-nop:2.0.9")
50+
}
51+
wasmJsMain.dependencies {}
52+
}
3553
}

0 commit comments

Comments
 (0)