#162 ## Motivation and Context - update readme - add more snippets - add knit ## How Has This Been Tested? knit on CI ## Breaking Changes None ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] Documentation update ## Checklist - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [ ] I have added appropriate error handling - [x] I have added or updated documentation as needed --------- Co-authored-by: devcrocod <devcrocod@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
7eb2a93c46
commit
adc4150f1f
12 changed files with 1093 additions and 195 deletions
12
.github/workflows/apidocs.yaml
vendored
12
.github/workflows/apidocs.yaml
vendored
|
|
@ -7,6 +7,10 @@ on:
|
|||
# Allow running this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 21
|
||||
JAVA_DISTRIBUTION: temurin
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
|
|
@ -25,11 +29,11 @@ jobs:
|
|||
submodules: true # Fetch Hugo themes (true OR recursive)
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
|
||||
- name: Set up JDK 21
|
||||
- name: Set up JDK ${{ env.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
distribution: ${{ env.JAVA_DISTRIBUTION }}
|
||||
cache: gradle
|
||||
|
||||
- name: Setup Gradle
|
||||
|
|
@ -37,7 +41,7 @@ jobs:
|
|||
|
||||
- name: Generate Dokka Site
|
||||
run: |-
|
||||
./gradlew clean dokkaGenerate
|
||||
./gradlew clean knit docs:classes docs:knitCheck dokkaGenerate
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
|
|
|
|||
34
.github/workflows/build.yml
vendored
34
.github/workflows/build.yml
vendored
|
|
@ -120,6 +120,40 @@ jobs:
|
|||
path: local-maven-repository.zip
|
||||
retention-days: 1
|
||||
|
||||
knit:
|
||||
runs-on: ubuntu-latest
|
||||
name: Knit
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up JDK ${{ env.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
distribution: ${{ env.JAVA_DISTRIBUTION }}
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
with:
|
||||
validate-wrappers: true
|
||||
add-job-summary: 'always'
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
gradle-home-cache-includes: |
|
||||
caches
|
||||
notifications
|
||||
sdks
|
||||
../.konan/**
|
||||
|
||||
- name: Run Knit
|
||||
run: |-
|
||||
./gradlew knit docs:classes docs:knitCheck -x :conformance-test:test --max-workers 3 --continue --rerun-tasks --configure-on-demand --parallel
|
||||
|
||||
|
||||
samples:
|
||||
needs: [ build ]
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
842
README.md
842
README.md
|
|
@ -1,6 +1,5 @@
|
|||
# MCP Kotlin SDK
|
||||
|
||||
|
||||
[](https://central.sonatype.com/artifact/io.modelcontextprotocol/kotlin-sdk)
|
||||
[](https://github.com/modelcontextprotocol/kotlin-sdk/actions/workflows/build.yml)
|
||||
[](https://github.com/modelcontextprotocol/kotlin-sdk/actions/workflows/conformance.yml)
|
||||
|
|
@ -9,100 +8,306 @@
|
|||
[](http://java.com)
|
||||
[](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.
|
||||
Kotlin Multiplatform SDK for the [Model Context Protocol](https://modelcontextprotocol.io).
|
||||
It enables Kotlin applications targeting JVM, Native, JS, and Wasm to implement MCP clients and servers using a
|
||||
standardized protocol interface.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Installation](#installation)
|
||||
* [Artifacts](#artifacts)
|
||||
* [Gradle setup (JVM)](#gradle-setup-jvm)
|
||||
* [Multiplatform](#multiplatform)
|
||||
* [Ktor dependencies](#ktor-dependencies)
|
||||
* [Quickstart](#quickstart)
|
||||
* [Creating a Client](#creating-a-client)
|
||||
* [Creating a Server](#creating-a-server)
|
||||
* [Core Concepts](#core-concepts)
|
||||
* [MCP Primitives](#mcp-primitives)
|
||||
* [Capabilities](#capabilities)
|
||||
* [Server Capabilities](#server-capabilities)
|
||||
* [Client Capabilities](#client-capabilities)
|
||||
* [Server Features](#server-features)
|
||||
* [Prompts](#prompts)
|
||||
* [Resources](#resources)
|
||||
* [Tools](#tools)
|
||||
* [Completion](#completion)
|
||||
* [Logging](#logging)
|
||||
* [Pagination](#pagination)
|
||||
* [Client Features](#client-features)
|
||||
* [Roots](#roots)
|
||||
* [Sampling](#sampling)
|
||||
* [Transports](#transports)
|
||||
* [STDIO Transport](#stdio-transport)
|
||||
* [Streamable HTTP Transport](#streamable-http-transport)
|
||||
* [SSE Transport](#sse-transport)
|
||||
* [WebSocket Transport](#websocket-transport)
|
||||
* [Connecting your server](#connecting-your-server)
|
||||
* [Examples](#examples)
|
||||
* [Documentation](#documentation)
|
||||
* [Contributing](#contributing)
|
||||
* [License](#license)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## 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 SDK implements the MCP specification for Kotlin,
|
||||
enabling you to build applications that can communicate using MCP on the JVM, WebAssembly and iOS.
|
||||
This Kotlin SDK implements the MCP specification, making it easy to:
|
||||
|
||||
- Build MCP clients that can connect to any MCP server
|
||||
- Create MCP servers that expose resources, prompts and tools
|
||||
- Use standard transports like stdio, SSE, and WebSocket
|
||||
- Handle all MCP protocol messages and lifecycle events
|
||||
|
||||
## Samples
|
||||
|
||||
- [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.
|
||||
- Build MCP **clients** that can connect to any MCP server
|
||||
- Create MCP **servers** that expose resources, prompts, and tools
|
||||
- Target **JVM, Native, JS, and Wasm** from a single codebase
|
||||
- Use standard transports like **stdio**, **SSE**, **Streamable HTTP**, and **WebSocket**
|
||||
- Handle MCP protocol messages and lifecycle events with coroutine-friendly APIs
|
||||
|
||||
## Installation
|
||||
|
||||
Add the new repository to your build file:
|
||||
### Artifacts
|
||||
|
||||
- `io.modelcontextprotocol:kotlin-sdk` – umbrella SDK (client + server APIs)
|
||||
- `io.modelcontextprotocol:kotlin-sdk-client` – client-only APIs
|
||||
- `io.modelcontextprotocol:kotlin-sdk-server` – server-only APIs
|
||||
|
||||
### Gradle setup (JVM)
|
||||
|
||||
Add the Maven Central repository and the SDK dependency:
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
```
|
||||
|
||||
Add the dependency:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
// See the badge above for the latest version
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
|
||||
}
|
||||
```
|
||||
MCP SDK uses [Ktor](https://ktor.io/), but does not come with a specific engine dependency.
|
||||
You should add [Ktor client](https://ktor.io/docs/client-dependencies.html#engine-dependency)
|
||||
and/or [Ktor server](https://ktor.io/docs/client-dependencies.html#engine-dependency) dependency
|
||||
to your project yourself, e.g.:
|
||||
|
||||
Use _kotlin-sdk-client_ or _kotlin-sdk-server_ if you only need one side of the API:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
// for client:
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
// for server:
|
||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk-client:$mcpVersion")
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk-server:$mcpVersion")
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
### Multiplatform
|
||||
|
||||
In a Kotlin Multiplatform project you can add the SDK to commonMain:
|
||||
|
||||
```kotlin
|
||||
commonMain {
|
||||
dependencies {
|
||||
// Works as a common dependency as well as the platform one
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ktor dependencies
|
||||
|
||||
The Kotlin MCP SDK uses [Ktor](https://ktor.io/), but it does not add Ktor engine dependencies transitively.
|
||||
You need to declare
|
||||
Ktor [client](https://ktor.io/docs/client-dependencies.html#engine-dependency)/[server](https://ktor.io/docs/server-dependencies.html)
|
||||
dependencies yourself (or reuse the ones already used in your project),
|
||||
for example:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
// MCP client with Ktor
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk-client:$mcpVersion")
|
||||
|
||||
// MCP server with Ktor
|
||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk-server:$mcpVersion")
|
||||
}
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
Let's create a simple MCP client and server to demonstrate the basic usage of the Kotlin SDK.
|
||||
|
||||
<!--- CLEAR -->
|
||||
|
||||
### Creating a Client
|
||||
|
||||
Create an MCP client that connects to a server via Streamable HTTP transport and lists available tools:
|
||||
|
||||
```kotlin
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.sse.SSE
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.Client
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.StdioClientTransport
|
||||
import io.modelcontextprotocol.kotlin.sdk.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.StreamableHttpClientTransport
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
val client = Client(
|
||||
clientInfo = Implementation(
|
||||
name = "example-client",
|
||||
version = "1.0.0"
|
||||
fun main(args: Array<String>) = runBlocking {
|
||||
val url = args.firstOrNull() ?: "http://localhost:3000/mcp"
|
||||
|
||||
val httpClient = HttpClient { install(SSE) }
|
||||
|
||||
val client = Client(
|
||||
clientInfo = Implementation(
|
||||
name = "example-client",
|
||||
version = "1.0.0"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val transport = StdioClientTransport(
|
||||
inputStream = processInputStream,
|
||||
outputStream = processOutputStream
|
||||
)
|
||||
val transport = StreamableHttpClientTransport(
|
||||
client = httpClient,
|
||||
url = url
|
||||
)
|
||||
|
||||
// Connect to server
|
||||
client.connect(transport)
|
||||
// Connect to server
|
||||
client.connect(transport)
|
||||
|
||||
// List available resources
|
||||
val resources = client.listResources()
|
||||
// List available tools
|
||||
val tools = client.listTools().tools
|
||||
|
||||
// Read a specific resource
|
||||
val resourceContent = client.readResource(
|
||||
ReadResourceRequest(uri = "file:///example.txt")
|
||||
)
|
||||
println(tools)
|
||||
}
|
||||
```
|
||||
|
||||
<!--- KNIT example-quickstart-client-01.kt -->
|
||||
|
||||
### Creating a Server
|
||||
|
||||
Create an MCP server that exposes a simple tool and runs on an embedded Ktor server with SSE transport:
|
||||
|
||||
```kotlin
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
|
||||
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.mcp
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val port = args.firstOrNull()?.toIntOrNull() ?: 3000
|
||||
val mcpServer = Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(listChanged = true),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
mcpServer.addTool(
|
||||
name = "example-tool",
|
||||
description = "An example tool",
|
||||
inputSchema = ToolSchema(
|
||||
properties = buildJsonObject {
|
||||
put("input", buildJsonObject { put("type", "string") })
|
||||
}
|
||||
)
|
||||
) { request ->
|
||||
CallToolResult(content = listOf(TextContent("Hello, world!")))
|
||||
}
|
||||
|
||||
embeddedServer(CIO, host = "127.0.0.1", port = port) {
|
||||
mcp {
|
||||
mcpServer
|
||||
}
|
||||
}.start(wait = true)
|
||||
}
|
||||
```
|
||||
|
||||
<!--- KNIT example-quickstart-server-01.kt -->
|
||||
|
||||
You can run the server and then connect to it using the client or test with the MCP Inspector:
|
||||
|
||||
```bash
|
||||
npx -y @modelcontextprotocol/inspector
|
||||
```
|
||||
|
||||
In the inspector UI, connect to `http://localhost:3000`.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### MCP Primitives
|
||||
|
||||
The MCP protocol defines core primitives that enable communication between servers and clients:
|
||||
|
||||
| Primitive | Server Role | Client Role | Description |
|
||||
|---------------|---------------------------------------------------|----------------------------------------|---------------------------------------------------|
|
||||
| **Prompts** | Provides prompt templates with optional arguments | Requests and uses prompts | Interactive templates for LLM interactions |
|
||||
| **Resources** | Exposes data sources (files, API responses, etc.) | Reads and subscribes to resources | Contextual data for augmenting LLM context |
|
||||
| **Tools** | Defines executable functions | Calls tools to perform actions | Functions the LLM can invoke to take actions |
|
||||
| **Sampling** | Requests LLM completions from client | Executes LLM calls and returns results | Server-initiated LLM requests (reverse direction) |
|
||||
|
||||
### Capabilities
|
||||
|
||||
Capabilities define what features a server or client supports. They are declared during initialization and determine
|
||||
what operations are available.
|
||||
|
||||
#### Server Capabilities
|
||||
|
||||
Servers declare their capabilities to inform clients what features they provide:
|
||||
|
||||
| Capability | Feature Flags | Description |
|
||||
|----------------|-------------------------------|------------------------------------------------------------|
|
||||
| `prompts` | `listChanged` | Prompt template management and notifications |
|
||||
| `resources` | `subscribe`<br/>`listChanged` | Resource exposure, subscriptions, and update notifications |
|
||||
| `tools` | `listChanged` | Tool discovery, execution, and list change notifications |
|
||||
| `logging` | - | Server logging to client console |
|
||||
| `completions` | - | Argument autocompletion suggestions |
|
||||
| `experimental` | Custom properties | Non-standard experimental features |
|
||||
|
||||
#### Client Capabilities
|
||||
|
||||
Clients declare their capabilities to inform servers what features they support:
|
||||
|
||||
| Capability | Feature Flags | Description |
|
||||
|----------------|-------------------|-------------------------------------------------------------|
|
||||
| `sampling` | - | Client can sample from an LLM (execute model requests) |
|
||||
| `roots` | `listChanged` | Client exposes root directories and can notify of changes |
|
||||
| `elicitation` | - | Client can display schema/form dialogs for structured input |
|
||||
| `experimental` | Custom properties | Non-standard experimental features |
|
||||
|
||||
### Server Features
|
||||
|
||||
The `Server` API lets you wire prompts, resources, and tools with only a few lines of Kotlin. Each feature is registered
|
||||
up front and then resolved lazily when a client asks for it, so your handlers stay small and suspendable.
|
||||
|
||||
#### Prompts
|
||||
|
||||
Prompts are user-controlled templates that clients discover via `prompts/list` and fetch with `prompts/get` when a user
|
||||
chooses one (think slash commands or saved flows). They’re best for repeatable, structured starters rather than ad-hoc
|
||||
model calls.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.PromptArgument
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.PromptMessage
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Role
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
|
||||
|
||||
fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
val server = Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-server",
|
||||
|
|
@ -110,97 +315,498 @@ val server = Server(
|
|||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(
|
||||
subscribe = true,
|
||||
listChanged = true
|
||||
)
|
||||
)
|
||||
prompts = ServerCapabilities.Prompts(listChanged = true),
|
||||
),
|
||||
)
|
||||
){
|
||||
"This server provides example resources and demonstrates MCP capabilities."
|
||||
)
|
||||
|
||||
server.addPrompt(
|
||||
name = "code-review",
|
||||
description = "Ask the model to review a diff",
|
||||
arguments = listOf(
|
||||
PromptArgument(name = "diff", description = "Unified diff", required = true),
|
||||
),
|
||||
) { request ->
|
||||
GetPromptResult(
|
||||
description = "Quick code review helper",
|
||||
messages = listOf(
|
||||
PromptMessage(
|
||||
role = Role.User,
|
||||
content = TextContent(text = "Review this change:\n${request.arguments?.get("diff")}"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-server-prompts-01.kt -->
|
||||
|
||||
Use prompts for anything that deserves a template: bug triage questions, onboarding checklists, or saved searches. Set
|
||||
`listChanged = true` only if your prompt catalog can change at runtime and your server will emit
|
||||
`notifications/prompts/list_changed` when it does.
|
||||
|
||||
#### Resources
|
||||
|
||||
Resources are application-driven context that clients discover via `resources/list` or `resources/templates/list`, then
|
||||
fetch with `resources/read`. Register each one with a stable URI and return a `ReadResourceResult` when asked—contents
|
||||
can be text or binary blobs.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents
|
||||
|
||||
fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
val server = Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(subscribe = true, listChanged = true),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
// Add a resource
|
||||
server.addResource(
|
||||
uri = "file:///example.txt",
|
||||
name = "Example Resource",
|
||||
description = "An example text file",
|
||||
mimeType = "text/plain"
|
||||
uri = "note://release/latest",
|
||||
name = "Release notes",
|
||||
description = "Last deployment summary",
|
||||
mimeType = "text/markdown",
|
||||
) { request ->
|
||||
ReadResourceResult(
|
||||
contents = listOf(
|
||||
TextResourceContents(
|
||||
text = "This is the content of the example resource.",
|
||||
text = "Ship 42 reached production successfully.",
|
||||
uri = request.uri,
|
||||
mimeType = "text/plain"
|
||||
)
|
||||
)
|
||||
mimeType = "text/markdown",
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Start server with stdio transport
|
||||
val transport = StdioServerTransport()
|
||||
server.connect(transport)
|
||||
```
|
||||
|
||||
### Using SSE Transport
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-server-resources-01.kt -->
|
||||
|
||||
Resources can be static text, generated JSON, or blobs—anything the client can surface to the user or inject into the
|
||||
model context. Set `subscribe = true` if you emit `notifications/resources/updated` for changes to specific URIs, and
|
||||
`listChanged = true` if you’ll send `notifications/resources/list_changed` when the catalog itself changes.
|
||||
|
||||
#### Tools
|
||||
|
||||
Tools are model-controlled capabilities the client exposes to the model. Clients discover them via `tools/list`, invoke
|
||||
them with `tools/call`, and your handlers receive JSON arguments, can emit streaming logs or progress, and return a
|
||||
`CallToolResult`. Keep a human in the loop for sensitive operations.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
fun main() {
|
||||
-->
|
||||
|
||||
Directly in Ktor's `Application`:
|
||||
```kotlin
|
||||
import io.ktor.server.application.*
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.mcp
|
||||
val server = Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(listChanged = true),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
fun Application.module() {
|
||||
mcp {
|
||||
Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-sse-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = null),
|
||||
resources = ServerCapabilities.Resources(subscribe = null, listChanged = null)
|
||||
)
|
||||
),
|
||||
) {
|
||||
"This SSE server provides prompts and resources via Server-Sent Events."
|
||||
}
|
||||
}
|
||||
server.addTool(
|
||||
name = "echo",
|
||||
description = "Return whatever the user sent back to them",
|
||||
) { request ->
|
||||
val text = request.arguments?.get("text")?.jsonPrimitive?.content ?: "(empty)"
|
||||
CallToolResult(content = listOf(TextContent(text = "Echo: $text")))
|
||||
}
|
||||
```
|
||||
|
||||
Inside a custom Ktor's `Route`:
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-server-tools-01.kt -->
|
||||
|
||||
Register as many tools as you need—long-running jobs can report progress via the request context, and tools can also
|
||||
trigger sampling (see below) when they need the client’s LLM. Set `listChanged = true` only if your tool catalog can
|
||||
change at runtime and your server will emit `notifications/tools/list_changed` when it does.
|
||||
|
||||
#### Completion
|
||||
|
||||
Completion provides argument suggestions for prompts or resource templates. Declare the `completions` capability and
|
||||
handle `completion/complete` requests to return up to 100 ranked values (include `total`/`hasMore` if you paginate).
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CompleteRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CompleteResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Method
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import kotlinx.io.asSink
|
||||
import kotlinx.io.asSource
|
||||
import kotlinx.io.buffered
|
||||
|
||||
suspend fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.sse.SSE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.mcp
|
||||
val server = Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
completions = ServerCapabilities.Completions,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
fun Application.module() {
|
||||
install(SSE)
|
||||
val session = server.createSession(
|
||||
StdioServerTransport(
|
||||
inputStream = System.`in`.asSource().buffered(),
|
||||
outputStream = System.out.asSink().buffered()
|
||||
)
|
||||
)
|
||||
|
||||
routing {
|
||||
route("myRoute") {
|
||||
mcp {
|
||||
Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-sse-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = null),
|
||||
resources = ServerCapabilities.Resources(subscribe = null, listChanged = null)
|
||||
)
|
||||
),
|
||||
) {
|
||||
"Connect via SSE to interact with this MCP server."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
session.setRequestHandler<CompleteRequest>(Method.Defined.CompletionComplete) { request, _ ->
|
||||
val options = listOf("kotlin", "compose", "coroutine")
|
||||
val matches = options.filter { it.startsWith(request.argument.value.lowercase()) }
|
||||
|
||||
CompleteResult(
|
||||
completion = CompleteResult.Completion(
|
||||
values = matches.take(3),
|
||||
total = matches.size,
|
||||
hasMore = matches.size > 3,
|
||||
),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-server-util-completions-01.kt -->
|
||||
|
||||
Use `context.arguments` to refine suggestions for dependent fields (e.g., framework list filtered by chosen language).
|
||||
|
||||
#### Logging
|
||||
|
||||
Logging lets the server stream structured log notifications to the client using RFC 5424 levels (`debug` → `emergency`).
|
||||
Declare the `logging` capability; clients can raise the minimum level with `logging/setLevel`, and the server emits
|
||||
`notifications/message` with severity, optional logger name, and JSON data.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.LoggingLevel
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.LoggingMessageNotification
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.LoggingMessageNotificationParams
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import kotlinx.io.asSink
|
||||
import kotlinx.io.asSource
|
||||
import kotlinx.io.buffered
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
suspend fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
val server = Server(
|
||||
serverInfo = Implementation("example-server", "1.0.0"),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
logging = ServerCapabilities.Logging,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val session = server.createSession(
|
||||
StdioServerTransport(
|
||||
inputStream = System.`in`.asSource().buffered(),
|
||||
outputStream = System.out.asSink().buffered()
|
||||
)
|
||||
)
|
||||
|
||||
session.sendLoggingMessage(
|
||||
LoggingMessageNotification(
|
||||
LoggingMessageNotificationParams(
|
||||
level = LoggingLevel.Info,
|
||||
logger = "startup",
|
||||
data = buildJsonObject { put("message", "Server started") },
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-server-util-logging-01.kt -->
|
||||
|
||||
Keep logs free of sensitive data, and expect clients to surface them in their own UI.
|
||||
|
||||
#### Pagination
|
||||
|
||||
List operations return paginated results with an opaque `nextCursor`, clients echo that cursor to fetch the next page.
|
||||
Supported list calls: `resources/list`, `resources/templates/list`, `prompts/list`, and `tools/list`.
|
||||
Treat cursors as opaque—don’t parse or persist them across sessions.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ListResourcesRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ListResourcesResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Method
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Resource
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
|
||||
import kotlinx.io.asSink
|
||||
import kotlinx.io.asSource
|
||||
import kotlinx.io.buffered
|
||||
|
||||
suspend fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
val server = Server(
|
||||
serverInfo = Implementation("example-server", "1.0.0"),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val session = server.createSession(
|
||||
StdioServerTransport(
|
||||
inputStream = System.`in`.asSource().buffered(),
|
||||
outputStream = System.out.asSink().buffered()
|
||||
)
|
||||
)
|
||||
|
||||
val resources = listOf(
|
||||
Resource(uri = "note://1", name = "Note 1", description = "First"),
|
||||
Resource(uri = "note://2", name = "Note 2", description = "Second"),
|
||||
Resource(uri = "note://3", name = "Note 3", description = "Third"),
|
||||
)
|
||||
val pageSize = 2
|
||||
|
||||
session.setRequestHandler<ListResourcesRequest>(Method.Defined.ResourcesList) { request, _ ->
|
||||
val start = request.params?.cursor?.toIntOrNull() ?: 0
|
||||
val page = resources.drop(start).take(pageSize)
|
||||
val next = if (start + page.size < resources.size) (start + page.size).toString() else null
|
||||
|
||||
ListResourcesResult(
|
||||
resources = page,
|
||||
nextCursor = next,
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-server-util-pagination-01.kt -->
|
||||
|
||||
Include `nextCursor` only when more items remain an absent cursor ends pagination.
|
||||
|
||||
### Client Features
|
||||
|
||||
Clients advertise their capabilities (roots, sampling, elicitation, etc.) during initialization. After that they can
|
||||
serve requests from the server while still initiating calls such as `listTools` or `callTool`.
|
||||
|
||||
#### Roots
|
||||
|
||||
Roots let the client declare where the server is allowed to operate. Declare the `roots` capability, respond to
|
||||
`roots/list`, and emit `notifications/roots/list_changed` if you set `listChanged = true`. URIs **must** be `file://`
|
||||
paths.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.Client
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.ClientOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
|
||||
suspend fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
val client = Client(
|
||||
clientInfo = Implementation("demo-client", "1.0.0"),
|
||||
options = ClientOptions(
|
||||
capabilities = ClientCapabilities(roots = ClientCapabilities.Roots(listChanged = true)),
|
||||
),
|
||||
)
|
||||
|
||||
client.addRoot(
|
||||
uri = "file:///Users/demo/projects",
|
||||
name = "Projects",
|
||||
)
|
||||
client.sendRootsListChanged()
|
||||
```
|
||||
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-client-roots-01.kt -->
|
||||
|
||||
Call `addRoot`/`removeRoot` whenever your file system view changes, and use `sendRootsListChanged()` to notify the
|
||||
server. Keep root lists user-controlled and revoke entries that are no longer authorized.
|
||||
|
||||
#### Sampling
|
||||
|
||||
Sampling lets the server ask the client to call its preferred LLM. Declare the `sampling` capability (and
|
||||
`sampling.tools` if you allow tool-enabled sampling), and handle `sampling/createMessage`. Keep a human in the loop for
|
||||
approvals.
|
||||
|
||||
<!--- INCLUDE
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.Client
|
||||
import io.modelcontextprotocol.kotlin.sdk.client.ClientOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Method
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Role
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
|
||||
fun main() {
|
||||
-->
|
||||
|
||||
```kotlin
|
||||
val client = Client(
|
||||
clientInfo = Implementation("demo-client", "1.0.0"),
|
||||
options = ClientOptions(
|
||||
capabilities = ClientCapabilities(
|
||||
sampling = buildJsonObject { putJsonObject("tools") { } }, // drop tools if you don't support tool use
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
client.setRequestHandler<CreateMessageRequest>(Method.Defined.SamplingCreateMessage) { request, _ ->
|
||||
val content = request.messages.lastOrNull()?.content
|
||||
val prompt = if (content is TextContent) content.text else "your topic"
|
||||
CreateMessageResult(
|
||||
model = "gpt-4o-mini",
|
||||
role = Role.Assistant,
|
||||
content = TextContent(text = "Here is a short note about $prompt"),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<!--- SUFFIX
|
||||
}
|
||||
-->
|
||||
|
||||
<!--- KNIT example-client-sampling-01.kt -->
|
||||
|
||||
Inside the handler you can pick any model/provider, require approvals, or reject the request. If you don’t support tool
|
||||
use, omit `sampling.tools` from capabilities.
|
||||
|
||||
[//]: # (TODO: add elicitation section)
|
||||
|
||||
[//]: # (#### Elicitation)
|
||||
|
||||
## Transports
|
||||
|
||||
All transports share the same API surface, so you can change deployment style without touching business logic. Pick the
|
||||
transport that best matches where the server runs.
|
||||
|
||||
### STDIO Transport
|
||||
|
||||
`StdioClientTransport` and `StdioServerTransport` tunnel MCP messages over stdin/stdout—perfect for editor plugins or
|
||||
CLI tooling that spawns a helper process. No networking setup is required.
|
||||
|
||||
### Streamable HTTP Transport
|
||||
|
||||
`StreamableHttpClientTransport` and the Ktor `streamableHttpApp()` helpers expose MCP over a single HTTP endpoint with
|
||||
optional JSON-only or SSE streaming responses. This is the recommended choice for remote deployments and integrates
|
||||
nicely with proxies or service meshes.
|
||||
|
||||
### SSE Transport
|
||||
|
||||
Server-Sent Events remain available for backwards compatibility with older MCP clients. Use `SseServerTransport` or the
|
||||
SSE Ktor plugin when you need drop-in compatibility, but prefer Streamable HTTP for new projects.
|
||||
|
||||
### WebSocket Transport
|
||||
|
||||
`WebSocketClientTransport` plus the matching server utilities provide full-duplex, low-latency connections—useful when
|
||||
you expect lots of notifications or long-running sessions behind a reverse proxy that already terminates WebSockets.
|
||||
|
||||
## Connecting your server
|
||||
|
||||
1. Start a sample HTTP server on port 3000:
|
||||
|
||||
```bash
|
||||
./gradlew :samples:kotlin-mcp-server:run
|
||||
```
|
||||
|
||||
2. Connect with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) or Claude Desktop/Code:
|
||||
|
||||
```bash
|
||||
npx -y @modelcontextprotocol/inspector --connect http://localhost:3000
|
||||
# or
|
||||
claude mcp add --transport http kotlin-mcp http://localhost:3000
|
||||
```
|
||||
|
||||
3. In the Inspector, confirm prompts, tools, resources, completions, and logs show up. Iterate locally until you’re
|
||||
ready to host the server wherever you prefer.
|
||||
|
||||
## Examples
|
||||
|
||||
| Scenario | Description | Example |
|
||||
|--------------------------|-----------------------------------------------------------------|--------------------------------------------------------------------------|
|
||||
| Streamable HTTP server | Full MCP server with prompts, resources, tools, completions | [samples/kotlin-mcp-server](./samples/kotlin-mcp-server) |
|
||||
| STDIO weather server | Minimal STDIO transport server exposing weather info and alerts | [samples/weather-stdio-server](./samples/weather-stdio-server) |
|
||||
| Interactive STDIO client | MCP client that connects over STDIO and pipes requests to LLMs | [samples/kotlin-mcp-client](./samples/kotlin-mcp-client) |
|
||||
| Streamable HTTP client | MCP client demo in a runnable notebook | [samples/notebooks/McpClient.ipynb](./samples/notebooks/McpClient.ipynb) |
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API Reference](https://modelcontextprotocol.github.io/kotlin-sdk/)
|
||||
- [Model Context Protocol documentation](https://modelcontextprotocol.io)
|
||||
- [MCP specification](https://modelcontextprotocol.io/specification/latest)
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see the [contribution guide](CONTRIBUTING.md) and the [Code of conduct](CODE_OF_CONDUCT.md) before contributing.
|
||||
|
|
|
|||
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
src/
|
||||
35
docs/build.gradle.kts
Normal file
35
docs/build.gradle.kts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
plugins {
|
||||
kotlin("jvm")
|
||||
alias(libs.plugins.knit)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":kotlin-sdk"))
|
||||
implementation(libs.ktor.server.cio)
|
||||
}
|
||||
|
||||
tasks.matching {
|
||||
it.name == "ktlintMainSourceSetCheck" || it.name == "ktlintMainSourceSetFormat"
|
||||
}.configureEach {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
knit {
|
||||
rootDir = project.rootDir
|
||||
files = files(project.rootDir.resolve("README.md"))
|
||||
defaultLineSeparator = "\n"
|
||||
siteRoot = "" // Disable site root validation
|
||||
}
|
||||
|
||||
// Only run knitCheck when explicitly requested, not as part of build/check
|
||||
tasks.named("knitCheck") {
|
||||
onlyIf {
|
||||
gradle.startParameter.taskNames.any {
|
||||
it.contains("knitCheck") || it.contains("knit")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.clean {
|
||||
delete("src")
|
||||
}
|
||||
|
|
@ -1,21 +1,17 @@
|
|||
# MCP Kotlin SDK
|
||||
|
||||
Kotlin SDK for the Model Context Protocol (MCP).
|
||||
This is a Kotlin Multiplatform library that helps you build MCP clients and servers that speak the same protocol and
|
||||
share the same types.
|
||||
The SDK focuses on clarity, small building blocks, and first‑class coroutine support.
|
||||
Kotlin Multiplatform implementation of the Model Context Protocol (MCP). The SDK focuses on clear, explicit APIs,
|
||||
small building blocks, and first-class coroutine support so clients and servers share the same well-typed messages and
|
||||
transports.
|
||||
|
||||
Use the umbrella `kotlin-sdk` artifact when you want a single dependency that brings the core types plus both client and
|
||||
server toolkits. If you only need one side, depend on `kotlin-sdk-client` or `kotlin-sdk-server` directly.
|
||||
|
||||
Gradle (Kotlin DSL):
|
||||
Use the umbrella `kotlin-sdk` artifact to bring in everything at once, or depend on the focused modules directly:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
// Convenience bundle with everything you need to start
|
||||
// All-in-one bundle
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk:<version>")
|
||||
|
||||
// Or pick modules explicitly
|
||||
// Or pick sides explicitly
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk-client:<version>")
|
||||
implementation("io.modelcontextprotocol:kotlin-sdk-server:<version>")
|
||||
}
|
||||
|
|
@ -25,79 +21,74 @@ dependencies {
|
|||
|
||||
## Module kotlin-sdk-core
|
||||
|
||||
Foundational, platform‑agnostic pieces:
|
||||
Foundation shared by both sides:
|
||||
|
||||
- Protocol data model and JSON serialization (kotlinx.serialization)
|
||||
- Request/response and notification types used by both sides of MCP
|
||||
- Coroutine‑friendly protocol engine and utilities
|
||||
- Transport abstractions shared by client and server
|
||||
- Protocol data model for MCP requests, results, notifications, capabilities, and content types.
|
||||
- `McpJson` (kotlinx.serialization) with MCP-friendly defaults plus helpers for converting native values to JSON.
|
||||
- Transport abstractions (`Transport`, `AbstractTransport`, `WebSocketMcpTransport`) and streaming `ReadBuffer`.
|
||||
- `Protocol` base class that handles JSON-RPC framing, correlation, progress tokens, and capability assertions.
|
||||
|
||||
You typically do not use `core` directly in application code; it is pulled in by the client/server modules. Use it
|
||||
explicitly if you only need the protocol types or plan to implement a custom transport.
|
||||
Use `core` when you need the raw types or want to author a custom transport. Public APIs are explicit to keep the shared
|
||||
surface stable across platforms.
|
||||
|
||||
---
|
||||
|
||||
## Module kotlin-sdk-client
|
||||
|
||||
High‑level client API for connecting to an MCP server and invoking its tools, prompts, and resources. Ships with several
|
||||
transports:
|
||||
High-level client for connecting to MCP servers and invoking their features:
|
||||
|
||||
- WebSocketClientTransport – low latency, full‑duplex
|
||||
- SSEClientTransport – Server‑Sent Events over HTTP
|
||||
- StdioClientTransport – CLI‑friendly stdio bridge
|
||||
- StreamableHttpClientTransport – simple HTTP streaming
|
||||
- `Client` runtime (or `mcpClient` helper) performs the MCP handshake and exposes `serverCapabilities`,
|
||||
`serverVersion`, and `serverInstructions`.
|
||||
- Typed operations for tools, prompts, resources, completion, logging, roots, sampling, and elicitation with capability
|
||||
enforcement.
|
||||
- Transports: `StdioClientTransport`, `SseClientTransport`, `WebSocketClientTransport`, and `StreamableHttpClientTransport`,
|
||||
plus Ktor client extensions for quick wiring.
|
||||
|
||||
A minimal client:
|
||||
Minimal WebSocket client:
|
||||
|
||||
```kotlin
|
||||
val client = Client(
|
||||
clientInfo = Implementation(name = "sample-client", version = "1.0.0")
|
||||
val client = mcpClient(
|
||||
clientInfo = Implementation("sample-client", "1.0.0"),
|
||||
clientOptions = ClientOptions(ClientCapabilities(tools = ClientCapabilities.Tools())),
|
||||
transport = WebSocketClientTransport("ws://localhost:8080/mcp")
|
||||
)
|
||||
|
||||
client.connect(WebSocketClientTransport("ws://localhost:8080/mcp"))
|
||||
|
||||
val tools = client.listTools()
|
||||
val result = client.callTool(
|
||||
name = "echo",
|
||||
arguments = mapOf("text" to "Hello, MCP!")
|
||||
)
|
||||
val result = client.callTool("echo", mapOf("text" to "Hello, MCP!"))
|
||||
println(result.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module kotlin-sdk-server
|
||||
|
||||
Lightweight server toolkit for hosting MCP tools, prompts, and resources. It provides a small, composable API and
|
||||
ready‑to‑use transports:
|
||||
Server toolkit for exposing MCP tools, prompts, and resources:
|
||||
|
||||
- StdioServerTransport – integrates well with CLIs and editors
|
||||
- SSE/WebSocket helpers for Ktor – easy HTTP deployment
|
||||
- `Server` runtime coordinates sessions, initialization flow, and capability enforcement with registries for tools,
|
||||
prompts, resources, and templates.
|
||||
- Transports: `StdioServerTransport` for CLI/editor bridges; Ktor extensions (`mcp` for SSE + POST back-channel and
|
||||
`mcpWebSocket` for WebSocket) for HTTP hosting.
|
||||
- Built-in notifications for list changes and resource subscriptions when capabilities enable them.
|
||||
|
||||
Register tools and run over stdio:
|
||||
Minimal Ktor SSE server:
|
||||
|
||||
```kotlin
|
||||
|
||||
val server = Server(
|
||||
serverInfo = Implementation(name = "sample-server", version = "1.0.0"),
|
||||
options = ServerOptions(ServerCapabilities())
|
||||
)
|
||||
|
||||
server.addTool(
|
||||
name = "echo",
|
||||
description = "Echoes the provided text"
|
||||
) { request ->
|
||||
// Build and return a CallToolResult from request.arguments
|
||||
// (see CallToolResult and related types in kotlin-sdk-core)
|
||||
/* ... */
|
||||
fun Application.module() {
|
||||
mcp {
|
||||
Server(
|
||||
serverInfo = Implementation("sample-server", "1.0.0"),
|
||||
options = ServerOptions(ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(listChanged = true),
|
||||
resources = ServerCapabilities.Resources(listChanged = true, subscribe = true),
|
||||
)),
|
||||
) {
|
||||
addTool(name = "echo", description = "Echo text back") { request ->
|
||||
val text = request.params.arguments?.get("text")?.jsonPrimitive?.content ?: ""
|
||||
CallToolResult(content = listOf(TextContent("You said: $text")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bridge the protocol over stdio
|
||||
val transport = StdioServerTransport(
|
||||
inputStream = kotlinx.io.files.Path("/dev/stdin").source(),
|
||||
outputStream = kotlinx.io.files.Path("/dev/stdout").sink()
|
||||
)
|
||||
// Start transport and wire it with the server using provided helpers in the SDK.
|
||||
```
|
||||
|
||||
For HTTP deployments, use the Ktor extensions included in the module to expose an MCP WebSocket or SSE endpoint with a
|
||||
few lines of code.
|
||||
Pick the module that matches your role, or use the umbrella artifact to get both sides with the shared core.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ dokka = "2.1.0"
|
|||
kotlin = "2.2.21"
|
||||
kover = "0.9.4"
|
||||
ktlint = "14.0.1"
|
||||
knit = "0.5.1"
|
||||
mavenPublish = "0.36.0"
|
||||
netty = "4.2.9.Final"
|
||||
openapi-generator = "7.18.0"
|
||||
|
|
@ -23,7 +24,7 @@ mockk = "1.14.7"
|
|||
mokksy = "0.6.2"
|
||||
serialization = "1.9.0"
|
||||
slf4j = "2.0.17"
|
||||
junit="5.14.2"
|
||||
junit = "5.14.2"
|
||||
|
||||
[libraries]
|
||||
# Plugins
|
||||
|
|
@ -74,4 +75,5 @@ detekt = { id = "dev.detekt", version.ref = "detekt" }
|
|||
kotlinx-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidatorPlugin" }
|
||||
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
|
||||
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
|
||||
knit = { id = "org.jetbrains.kotlinx.knit", version.ref = "knit" }
|
||||
openapi-generator = { id = "org.openapi.generator", version.ref = "openapi-generator" }
|
||||
|
|
|
|||
3
knit.properties
Normal file
3
knit.properties
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Knit configuration
|
||||
knit.dir=docs/src/main/kotlin/
|
||||
knit.package=io.modelcontextprotocol.kotlin.sdk.examples
|
||||
|
|
@ -1,10 +1,78 @@
|
|||
# Module kotlin-sdk-client
|
||||
|
||||
The Model Context Protocol (MCP) Kotlin SDK client module provides a multiplatform implementation for connecting to and
|
||||
interacting with MCP servers.
|
||||
`kotlin-sdk-client` is the multiplatform MCP client for Kotlin. It handles the MCP handshake, enforces capabilities, and
|
||||
gives you typed APIs to call tools, prompts, resources, completions, and logging endpoints on MCP servers. It reuses
|
||||
protocol types from `kotlin-sdk-core` and ships with transports for CLI and Ktor-based networking.
|
||||
|
||||
## Features
|
||||
## What the module provides
|
||||
|
||||
- Cross-platform support for JVM, WASM, JS, and Native
|
||||
- Built-in support for multiple transport protocols (stdio, SSE, WebSocket)
|
||||
- Type-safe API for resource management and server interactions
|
||||
- **Client runtime**: The `Client` class (or `mcpClient` helper) wraps initialization, capability checks, and
|
||||
request/notification plumbing. After connecting, it exposes `serverCapabilities`, `serverVersion`, and
|
||||
`serverInstructions`.
|
||||
- **Typed operations**: Convenience functions like `listTools`, `callTool`, `listResources`, `readResource`,
|
||||
`listPrompts`, `complete`, `setLoggingLevel`, and subscription helpers for resources.
|
||||
- **Capability enforcement**: Methods fail fast if the server does not advertise the needed capability; optional
|
||||
strictness mirrors the protocol options used by the server SDK.
|
||||
- **Transports**: Ready-to-use implementations for STDIO (CLI interoperability), Ktor SSE (`SseClientTransport` with
|
||||
POST back-channel), Ktor WebSocket, and Streamable HTTP for environments that prefer request/response streaming.
|
||||
- **Ktor client integration**: Extension helpers wire MCP over Ktor client engines with minimal setup for SSE,
|
||||
Streamable HTTP, or WebSocket.
|
||||
|
||||
## Typical client setup
|
||||
|
||||
1. Declare client capabilities with `ClientOptions` (e.g., sampling, roots, elicitation, experimental).
|
||||
2. Create a transport suitable for your environment.
|
||||
3. Connect using `mcpClient` or instantiate `Client` and call `connect(transport)`.
|
||||
4. Use typed APIs to interact with the server; results and errors use the shared MCP types.
|
||||
|
||||
### Minimal STDIO client
|
||||
|
||||
```kotlin
|
||||
val client = mcpClient(
|
||||
clientInfo = Implementation(name = "demo-client", version = "1.0.0"),
|
||||
clientOptions = ClientOptions(
|
||||
capabilities = ClientCapabilities(
|
||||
tools = ClientCapabilities.Tools(),
|
||||
resources = ClientCapabilities.Resources(subscribe = true),
|
||||
)
|
||||
),
|
||||
transport = StdioClientTransport(
|
||||
System.`in`.asSource().buffered(),
|
||||
System.out.asSink().buffered()
|
||||
)
|
||||
)
|
||||
|
||||
val tools = client.listTools()
|
||||
val result = client.callTool("hello", mapOf("name" to "Kotlin"))
|
||||
println(result.content)
|
||||
```
|
||||
|
||||
### Ktor-based transports
|
||||
|
||||
- **SSE** (streaming GET + POST back-channel): create `SseClientTransport(httpClient, url)` (or
|
||||
`HttpClient.mcpSseTransport(url)`); sessions are keyed by `sessionId` managed by the SDK.
|
||||
- **WebSocket**: use `WebSocketClientTransport(httpClient, url)` (or `HttpClient.mcpWebSocketTransport(url)`) to get
|
||||
bidirectional messaging in one connection.
|
||||
- **Streamable HTTP**: `StreamableHttpClientTransport(httpClient, url)` enables MCP over streaming HTTP
|
||||
(or `HttpClient.mcpStreamableHttpTransport(url)`).
|
||||
|
||||
## Feature usage highlights
|
||||
|
||||
- **Tools**: `callTool` accepts raw maps or `CallToolRequest`; metadata keys are validated per MCP rules.
|
||||
- **Prompts & completion**: `getPrompt`, `listPrompts`, and `complete` wrap common prompt flows.
|
||||
- **Resources**: `listResources`, `listResourceTemplates`, `readResource`, and `subscribeResource` cover discovery,
|
||||
templating, and updates (when the server allows subscriptions).
|
||||
- **Roots**: If enabled, register local roots with `addRoot`/`addRoots`; the client responds to `RootsList` requests
|
||||
from the server.
|
||||
- **Notifications**: Built-in handlers cover initialization, cancellation, progress, and roots list changes; you can
|
||||
register more handlers via the underlying `Protocol` API.
|
||||
|
||||
## Diagnostics and lifecycle
|
||||
|
||||
- Capability assertions prevent calling endpoints the server does not expose; errors surface with clear messages.
|
||||
- Logging uses `KotlinLogging`; enable DEBUG to inspect transport and handshake behavior.
|
||||
- `close()` tears down the transport and cancels in-flight requests.
|
||||
|
||||
Use this module whenever you need an MCP-aware Kotlin client—whether embedding in a CLI over STDIO or talking to remote
|
||||
servers via Ktor SSE/WebSocket/streamable HTTP transports. Public APIs are explicit so downstream users can rely on
|
||||
stable, well-typed surface areas across platforms.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,47 @@
|
|||
# Module kotlin-sdk-core
|
||||
|
||||
The MCP Kotlin SDK Core module provides fundamental functionality for interacting with Model Context Protocol (MCP).
|
||||
This module serves as the foundation for building MCP-enabled applications in Kotlin.
|
||||
`kotlin-sdk-core` is the foundation of the MCP Kotlin SDK. It contains protocol message types, JSON handling, and
|
||||
transport abstractions used by both client and server modules. No platform-specific code lives here; everything is
|
||||
designed for Kotlin Multiplatform with explicit API mode enabled.
|
||||
|
||||
## What the module provides
|
||||
|
||||
- **Protocol model**: Complete MCP data classes for requests, results, notifications, capabilities, tools, prompts,
|
||||
resources, logging, completion, sampling, elicitation, and roots. DSL helpers (`*.dsl.kt`) let you build messages
|
||||
fluently without repeating boilerplate.
|
||||
- **JSON utilities**: A shared `McpJson` configuration (kotlinx.serialization) with MCP-friendly settings (ignore
|
||||
unknown keys, no class discriminator). Helpers for converting maps to `JsonElement`, `EmptyJsonObject`, and
|
||||
encoding/decoding JSON-RPC envelopes.
|
||||
- **Transport abstractions**: `Transport` and `AbstractTransport` define the message pipeline, callbacks, and error
|
||||
handling. `WebSocketMcpTransport` adds a shared WebSocket implementation for both client and server sides, and
|
||||
`ReadBuffer` handles streaming JSON-RPC framing.
|
||||
- **Protocol engine**: The `Protocol` base class manages request/response correlation, notifications, progress tokens,
|
||||
and capability assertions. Higher-level modules extend it to become `Client` and `Server`.
|
||||
- **Errors and safety**: Common exception types (`McpException`, parsing errors) plus capability enforcement hooks
|
||||
ensure callers cannot use endpoints the peer does not advertise.
|
||||
|
||||
## Typical usage
|
||||
|
||||
- Import MCP types to define capabilities and message payloads for your own transports or custom integrations.
|
||||
- Extend `Protocol` if you need a bespoke peer role while reusing correlation logic and JSON-RPC helpers.
|
||||
- Use the DSL builders to construct well-typed requests (tools, prompts, resources, logging, completion) instead of
|
||||
crafting raw JSON.
|
||||
|
||||
### Example: building a tool call request
|
||||
|
||||
```kotlin
|
||||
val request = CallToolRequest {
|
||||
name = "summarize"
|
||||
arguments = mapOf("text" to "Hello MCP")
|
||||
}
|
||||
```
|
||||
|
||||
### Example: parsing a JSON-RPC message
|
||||
|
||||
```kotlin
|
||||
val message: JSONRPCMessage = McpJson.decodeFromString(jsonString)
|
||||
```
|
||||
|
||||
Use this module when you need the raw building blocks of MCP—types, JSON config, and transport base classes—whether to
|
||||
embed in another runtime, author new transports, or contribute higher-level features in the client/server modules. The
|
||||
APIs are explicit to keep the shared surface stable for downstream users.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,125 @@
|
|||
# Module kotlin-sdk-server
|
||||
|
||||
The server module provides core server-side functionality for the Model Context Protocol (MCP) Kotlin SDK.
|
||||
This module includes essential parts for writing MCP server applications.
|
||||
`kotlin-sdk-server` contains the server-side building blocks for Model Context Protocol (MCP) applications written in
|
||||
Kotlin Multiplatform. It pairs the protocol types from `kotlin-sdk-core` with transport implementations and utilities
|
||||
that make it straightforward to expose tools, prompts, and resources to MCP clients.
|
||||
|
||||
## Features
|
||||
## What the module provides
|
||||
|
||||
* Server-side request handling
|
||||
* Model context management
|
||||
* Protocol implementation
|
||||
* Integration utilities
|
||||
- **Server runtime**: The `Server` class coordinates sessions, initialization flow, and capability enforcement. It
|
||||
tracks active sessions and cleans them up when transports close.
|
||||
- **Feature registries**: Helpers to register and remove tools, prompts, resources, and resource templates. Registry
|
||||
listeners emit list-changed notifications when the capability is enabled.
|
||||
- **Transports**: Ready-to-use transports for STDIO (CLI-style hosting), Ktor Server-Sent Events (SSE) with POST
|
||||
back-channel, and Ktor WebSocket. All share the common `Transport` abstraction from the SDK.
|
||||
- **Ktor integration**: Extension functions (`Routing.mcp`, `Routing.mcpWebSocket`, and `Application` variants) that
|
||||
plug MCP into an existing Ktor server with minimal boilerplate.
|
||||
- **Notifications**: Built-in support for resource subscription, resource-updated events, and feature list change
|
||||
notifications when capabilities are set accordingly.
|
||||
|
||||
For detailed usage instructions and examples, please refer to
|
||||
the [documentation](https://github.com/modelcontextprotocol/kotlin-sdk/blob/main/README.md).
|
||||
## Typical server setup
|
||||
|
||||
1. Declare capabilities your server supports using `ServerOptions`. Capabilities drive which handlers are installed and
|
||||
whether list-change notifications are emitted.
|
||||
2. Register features: tools, prompts, resources, and resource templates. Handlers run in suspending context and can be
|
||||
removed or replaced at runtime.
|
||||
3. Attach a transport and open a session via `createSession(transport)`. Multiple sessions can be active simultaneously.
|
||||
4. Close the server to tear down transports and unsubscribe sessions.
|
||||
|
||||
### Minimal STDIO server
|
||||
|
||||
```kotlin
|
||||
val server = Server(
|
||||
serverInfo = Implementation(name = "demo-server", version = "1.0.0"),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(listChanged = true)
|
||||
)
|
||||
)
|
||||
) {
|
||||
addTool(
|
||||
name = "hello",
|
||||
description = "Greets the caller by name"
|
||||
) { request ->
|
||||
val name = request.params.arguments?.get("name")?.jsonPrimitive?.content ?: "there"
|
||||
CallToolResult(
|
||||
content = listOf(TextContent("Hello, $name!")),
|
||||
isError = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val transport = StdioServerTransport(
|
||||
System.`in`.asSource().buffered(),
|
||||
System.out.asSink().buffered()
|
||||
)
|
||||
server.createSession(transport)
|
||||
```
|
||||
|
||||
### Hosting with Ktor (SSE)
|
||||
|
||||
```kotlin
|
||||
fun Application.module() {
|
||||
mcp {
|
||||
Server(
|
||||
serverInfo = Implementation("ktor-sse", "1.0.0"),
|
||||
options = ServerOptions(
|
||||
ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = true),
|
||||
resources = ServerCapabilities.Resources(listChanged = true, subscribe = true),
|
||||
)
|
||||
)
|
||||
) {
|
||||
addPrompt(
|
||||
name = "welcome",
|
||||
description = "Explain how to use the server"
|
||||
) {
|
||||
GetPromptResult(
|
||||
description = "Welcome to the MCP server",
|
||||
messages = listOf(CreateMessageResult.Message(TextContent("Try listing tools first")))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The SSE route exposes a streaming GET endpoint and a POST endpoint for client messages. Session affinity is maintained
|
||||
by a `sessionId` query parameter managed by the SDK.
|
||||
|
||||
### Hosting with Ktor (WebSocket)
|
||||
|
||||
```kotlin
|
||||
fun Application.module() {
|
||||
mcpWebSocket {
|
||||
Server(
|
||||
serverInfo = Implementation("ktor-ws", "1.0.0"),
|
||||
options = ServerOptions(ServerCapabilities(tools = ServerCapabilities.Tools()))
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`mcpWebSocket` installs Ktor WebSockets and wires message handling over a single bidirectional connection.
|
||||
|
||||
## Feature registration details
|
||||
|
||||
- **Tools**: `addTool` accepts a `Tool` descriptor or inline parameters plus a suspend handler. `removeTool` and
|
||||
`addTools/removeTools` support dynamic updates.
|
||||
- **Prompts**: `addPrompt` wires a provider that returns `GetPromptResult` and optional arguments metadata.
|
||||
- **Resources**: `addResource` exposes static or generated content via `ReadResourceResult`; `addResources`
|
||||
batch-registers. Templates and roots are also supported for structured discovery.
|
||||
- **Instructions**: Provide static text or a function via `instructionsProvider` to send per-session usage guidance
|
||||
during initialization.
|
||||
|
||||
## Lifecycle and diagnostics
|
||||
|
||||
- Sessions emit callbacks via `onConnect` and `onClose`. Deprecated `onInitialized` exists for backward compatibility;
|
||||
prefer session-level hooks.
|
||||
- The server enforces declared capabilities; attempting to register an unsupported feature throws with a descriptive
|
||||
message.
|
||||
- Logging uses `KotlinLogging`; avoid logging secrets. To inspect traffic, run the server with DEBUG level enabled.
|
||||
|
||||
Use this module whenever you need to expose MCP features from a Kotlin service—whether embedding in a CLI via STDIO,
|
||||
serving from Ktor over SSE/WebSocket, or composing transports for custom environments. Public APIs are explicit to keep
|
||||
server-facing contracts stable for consumers.
|
||||
|
|
|
|||
|
|
@ -24,5 +24,6 @@ include(
|
|||
":kotlin-sdk-server",
|
||||
":kotlin-sdk",
|
||||
":integration-test",
|
||||
":docs",
|
||||
":conformance-test",
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue