Skip to content
Open
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
8 changes: 8 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ For background on the protocol itself, see the [MCP documentation](https://model
| Sample | Type | Transport | MCP Features |
|--------------------------------------------------------|-------------------|-----------------|------------------------------------|
| [simple-streamable-server](./simple-streamable-server) | Server | Streamable HTTP | Tools, Resources, Prompts, Logging |
| [kotlinlang-mcp-server](./kotlinlang-mcp-server) | Server | Streamable HTTP | Tools |
| [kotlin-mcp-server](./kotlin-mcp-server) | Server | STDIO, SSE | Tools, Resources, Prompts |
| [weather-stdio-server](./weather-stdio-server) | Server | STDIO | Tools |
| [kotlin-mcp-client](./kotlin-mcp-client) | Client | STDIO | Tool discovery & invocation |
Expand All @@ -30,6 +31,13 @@ A minimal Streamable HTTP server with optional Bearer token authentication. Demo
(`greet`, `multi-greet`), a prompt template, a resource, and server-to-client logging notifications.
[Read more →](./simple-streamable-server)

### Kotlinlang MCP Server

A Streamable HTTP server that exposes the official Kotlin documentation (kotlinlang.org) to LLM
clients — full-text search via Algolia and page retrieval in markdown. Demonstrates wrapping a real
external API in an MCP server with in-memory caching.
[Read more →](./kotlinlang-mcp-server)

### Kotlin MCP Server

A multi-transport server supporting STDIO, SSE (plain), and SSE (Ktor plugin). Useful for exploring
Expand Down
14 changes: 14 additions & 0 deletions samples/kotlinlang-mcp-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM gradle:jdk21-alpine AS build
WORKDIR /project
COPY gradle/ gradle/
COPY gradlew settings.gradle.kts build.gradle.kts ./
COPY src/ src/
RUN ./gradlew shadowJar --no-daemon

FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=build /project/build/libs/*-all.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
105 changes: 105 additions & 0 deletions samples/kotlinlang-mcp-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Kotlinlang MCP Server

A Streamable HTTP MCP server that exposes the official
[Kotlin documentation](https://kotlinlang.org/docs/) to LLM clients — full-text search via
[Algolia](https://www.algolia.com/) plus page retrieval in markdown through kotlinlang.org's
`_llms` endpoints.

## Overview

This sample demonstrates an MCP server that wraps a real external documentation source. It uses the
recommended Streamable HTTP transport, supports multiple concurrent client sessions, and caches
responses in memory to reduce upstream load. The server is intentionally read-only — it exposes two
tools, no prompts, no resources.

## Prerequisites

- JDK 21+
- Algolia credentials for `kotlinlang.org` (exported as environment variables, see
[Configuration](#configuration))

## Build & Run

```shell
export ALGOLIA_APP_ID=...
export ALGOLIA_API_KEY=...
export ALGOLIA_INDEX_NAME=...

./gradlew run
```

The server starts on `http://localhost:8080/mcp` by default.

Connect with the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector):

```shell
npx @modelcontextprotocol/inspector
```

In the Inspector UI, select **Streamable HTTP** transport and enter `http://localhost:8080/mcp`.

## MCP Capabilities

### Tools

| Name | Description |
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| `search_kotlinlang` | Full-text search across Kotlin documentation. Returns up to 5 results filtered to `/docs/` pages. Responses cached for 10 minutes. |
| `get_kotlinlang_page` | Fetches a documentation page as markdown via kotlinlang.org's `_llms` endpoint. Accepts a path relative to `/docs/`. Cached for 1 hour. |

Example paths accepted by `get_kotlinlang_page`: `coroutines-overview`,
`multiplatform/compose-multiplatform-and-jetpack-compose`. Leading/trailing slashes and an optional
`.html` suffix are normalized before resolution.

## Configuration

| Variable | Required | Default | Description |
|----------------------|----------|-----------|--------------------------|
| `ALGOLIA_APP_ID` | yes | — | Algolia application ID |
| `ALGOLIA_API_KEY` | yes | — | Algolia search API key |
| `ALGOLIA_INDEX_NAME` | yes | — | Algolia index name |
| `SERVER_PORT` | no | `8080` | HTTP server port |
| `SERVER_HOST` | no | `0.0.0.0` | HTTP server bind address |

The server does not provide fallback values for `ALGOLIA_*`. Startup fails if any of those
variables are missing.

## Docker

Multi-stage build with Alpine-based images. Runs as a non-root user.

```shell
docker build -t kotlinlang-mcp-server .

docker run \
-p 8080:8080 \
-e ALGOLIA_APP_ID=... \
-e ALGOLIA_API_KEY=... \
-e ALGOLIA_INDEX_NAME=... \
kotlinlang-mcp-server
```

## Connecting an MCP client

Any MCP client that supports Streamable HTTP transport can connect:

```json
{
"mcpServers": {
"kotlinlang": {
"url": "http://localhost:8080/mcp"
}
}
}
```

## Limitations

This sample is intended **for demonstration only**. Before production use, consider:

- **In-memory cache only** — cached data is lost on server restart.
- **No authentication or authorization** — run behind a trusted proxy or restrict network access.
- **No rate limiting** on outgoing requests to Algolia and kotlinlang.org.
- **Permissive CORS** (`anyHost()`) — restrict to specific origins for any non-local deployment.
- **Streamable HTTP only** — no STDIO transport for local-only usage.
- **Tools only** — no MCP resources or prompts are exposed.
42 changes: 42 additions & 0 deletions samples/kotlinlang-mcp-server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.shadow)
application
}

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

application {
mainClass.set("org.kotlinlang.mcp.ApplicationKt")
}

dependencies {
implementation(platform(libs.ktor.bom))
implementation(libs.mcp.kotlin.server)
implementation(libs.ktor.server.netty)
implementation(libs.ktor.server.sse)
implementation(libs.ktor.server.content.negotiation)
implementation(libs.ktor.server.cors)
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.logback.classic)

testImplementation(kotlin("test"))
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.ktor.client.mock)
testImplementation(libs.mcp.kotlin.client)
testImplementation(libs.mcp.kotlin.testing)
}

tasks.test {
useJUnitPlatform()
}

kotlin {
jvmToolchain(21)
}
4 changes: 4 additions & 0 deletions samples/kotlinlang-mcp-server/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
org.gradle.configuration-cache=true
org.gradle.parallel=true
org.gradle.caching=true

31 changes: 31 additions & 0 deletions samples/kotlinlang-mcp-server/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[versions]
kotlin = "2.3.20"
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sample pins Kotlin to 2.3.20, while the other samples in this repo currently standardize on 2.2.21 (e.g., samples/kotlin-mcp-server/gradle/libs.versions.toml:2, samples/simple-streamable-server/gradle/libs.versions.toml:2). Using a different Kotlin version for a single sample increases maintenance overhead and can introduce inconsistent behavior across samples; consider aligning this version unless there’s a specific need to demonstrate a newer Kotlin release.

Suggested change
kotlin = "2.3.20"
kotlin = "2.2.21"

Copilot uses AI. Check for mistakes.
ktor = "3.3.3"
mcp-kotlin = "0.11.1"
serialization = "1.10.0"
coroutines = "1.10.2"
logback = "1.5.32"
shadow = "9.4.1"

[libraries]
ktor-bom = { group = "io.ktor", name = "ktor-bom", version.ref = "ktor" }
ktor-server-netty = { group = "io.ktor", name = "ktor-server-netty" }
ktor-server-sse = { group = "io.ktor", name = "ktor-server-sse" }
ktor-server-content-negotiation = { group = "io.ktor", name = "ktor-server-content-negotiation" }
ktor-server-cors = { group = "io.ktor", name = "ktor-server-cors" }
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio" }
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation" }
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json" }
mcp-kotlin-server = { group = "io.modelcontextprotocol", name = "kotlin-sdk-server", version.ref = "mcp-kotlin" }
mcp-kotlin-client = { group = "io.modelcontextprotocol", name = "kotlin-sdk-client", version.ref = "mcp-kotlin" }
mcp-kotlin-testing = { group = "io.modelcontextprotocol", name = "kotlin-sdk-testing", version.ref = "mcp-kotlin" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading