Skip to content

Commit c946dfc

Browse files
authored
sample: kotlinlang mcp server (#713)
add new sample: kotlinlang mcp server with 2 tools - search - get_page ## How Has This Been Tested? inspector/unit ## Breaking Changes none ## 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 - [x] I have added appropriate error handling - [x] I have added or updated documentation as needed
1 parent bf8dc6d commit c946dfc

32 files changed

Lines changed: 2149 additions & 0 deletions

samples/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ For background on the protocol itself, see the [MCP documentation](https://model
99
| Sample | Type | Transport | MCP Features |
1010
|--------------------------------------------------------|-------------------|-----------------|------------------------------------|
1111
| [simple-streamable-server](./simple-streamable-server) | Server | Streamable HTTP | Tools, Resources, Prompts, Logging |
12+
| [kotlinlang-mcp-server](./kotlinlang-mcp-server) | Server | Streamable HTTP | Tools |
1213
| [kotlin-mcp-server](./kotlin-mcp-server) | Server | STDIO, SSE | Tools, Resources, Prompts |
1314
| [weather-stdio-server](./weather-stdio-server) | Server | STDIO | Tools |
1415
| [kotlin-mcp-client](./kotlin-mcp-client) | Client | STDIO | Tool discovery & invocation |
@@ -30,6 +31,13 @@ A minimal Streamable HTTP server with optional Bearer token authentication. Demo
3031
(`greet`, `multi-greet`), a prompt template, a resource, and server-to-client logging notifications.
3132
[Read more →](./simple-streamable-server)
3233

34+
### Kotlinlang MCP Server
35+
36+
A Streamable HTTP server that exposes the official Kotlin documentation (kotlinlang.org) to LLM
37+
clients — full-text search via Algolia and page retrieval in markdown. Demonstrates wrapping a real
38+
external API in an MCP server with in-memory caching.
39+
[Read more →](./kotlinlang-mcp-server)
40+
3341
### Kotlin MCP Server
3442

3543
A multi-transport server supporting STDIO, SSE (plain), and SSE (Ktor plugin). Useful for exploring
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM gradle:jdk21-alpine AS build
2+
WORKDIR /project
3+
COPY gradle/ gradle/
4+
COPY gradlew settings.gradle.kts build.gradle.kts ./
5+
COPY src/ src/
6+
RUN ./gradlew shadowJar --no-daemon
7+
8+
FROM eclipse-temurin:21-jre-alpine
9+
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
10+
WORKDIR /app
11+
COPY --from=build /project/build/libs/*-all.jar app.jar
12+
USER appuser
13+
EXPOSE 8080
14+
ENTRYPOINT ["java", "-jar", "app.jar"]
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Kotlinlang MCP Server
2+
3+
A Streamable HTTP MCP server that exposes the official
4+
[Kotlin documentation](https://kotlinlang.org/docs/) to LLM clients — full-text search via
5+
[Algolia](https://www.algolia.com/) plus page retrieval in markdown through kotlinlang.org's
6+
`_llms` endpoints.
7+
8+
## Overview
9+
10+
This sample demonstrates an MCP server that wraps a real external documentation source. It uses the
11+
recommended Streamable HTTP transport, supports multiple concurrent client sessions, and caches
12+
responses in memory to reduce upstream load. The server is intentionally read-only — it exposes two
13+
tools, no prompts, no resources.
14+
15+
## Prerequisites
16+
17+
- JDK 21+
18+
- Algolia credentials for `kotlinlang.org` (exported as environment variables, see
19+
[Configuration](#configuration))
20+
21+
## Build & Run
22+
23+
```shell
24+
export ALGOLIA_APP_ID=...
25+
export ALGOLIA_API_KEY=...
26+
export ALGOLIA_INDEX_NAME=...
27+
28+
./gradlew run
29+
```
30+
31+
The server starts on `http://localhost:8080/mcp` by default.
32+
33+
Connect with the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector):
34+
35+
```shell
36+
npx @modelcontextprotocol/inspector
37+
```
38+
39+
In the Inspector UI, select **Streamable HTTP** transport and enter `http://localhost:8080/mcp`.
40+
41+
## MCP Capabilities
42+
43+
### Tools
44+
45+
| Name | Description |
46+
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------|
47+
| `search_kotlinlang` | Full-text search across Kotlin documentation. Returns up to 5 results filtered to `/docs/` pages. Responses cached for 10 minutes. |
48+
| `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. |
49+
50+
Example paths accepted by `get_kotlinlang_page`: `coroutines-overview`,
51+
`multiplatform/compose-multiplatform-and-jetpack-compose`. Leading/trailing slashes and an optional
52+
`.html` suffix are normalized before resolution.
53+
54+
## Configuration
55+
56+
| Variable | Required | Default | Description |
57+
|----------------------|----------|-----------|--------------------------|
58+
| `ALGOLIA_APP_ID` | yes || Algolia application ID |
59+
| `ALGOLIA_API_KEY` | yes || Algolia search API key |
60+
| `ALGOLIA_INDEX_NAME` | yes || Algolia index name |
61+
| `SERVER_PORT` | no | `8080` | HTTP server port |
62+
| `SERVER_HOST` | no | `0.0.0.0` | HTTP server bind address |
63+
64+
The server does not provide fallback values for `ALGOLIA_*`. Startup fails if any of those
65+
variables are missing.
66+
67+
## Docker
68+
69+
Multi-stage build with Alpine-based images. Runs as a non-root user.
70+
71+
```shell
72+
docker build -t kotlinlang-mcp-server .
73+
74+
docker run \
75+
-p 8080:8080 \
76+
-e ALGOLIA_APP_ID=... \
77+
-e ALGOLIA_API_KEY=... \
78+
-e ALGOLIA_INDEX_NAME=... \
79+
kotlinlang-mcp-server
80+
```
81+
82+
## Connecting an MCP client
83+
84+
Any MCP client that supports Streamable HTTP transport can connect:
85+
86+
```json
87+
{
88+
"mcpServers": {
89+
"kotlinlang": {
90+
"url": "http://localhost:8080/mcp"
91+
}
92+
}
93+
}
94+
```
95+
96+
## Limitations
97+
98+
This sample is intended **for demonstration only**. Before production use, consider:
99+
100+
- **In-memory cache only** — cached data is lost on server restart.
101+
- **No authentication or authorization** — run behind a trusted proxy or restrict network access.
102+
- **No rate limiting** on outgoing requests to Algolia and kotlinlang.org.
103+
- **Permissive CORS** (`anyHost()`) — restrict to specific origins for any non-local deployment.
104+
- **Streamable HTTP only** — no STDIO transport for local-only usage.
105+
- **Tools only** — no MCP resources or prompts are exposed.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
plugins {
2+
alias(libs.plugins.kotlin.jvm)
3+
alias(libs.plugins.kotlin.serialization)
4+
alias(libs.plugins.shadow)
5+
application
6+
}
7+
8+
group = "org.example"
9+
version = "0.1.0"
10+
11+
application {
12+
mainClass.set("org.kotlinlang.mcp.ApplicationKt")
13+
}
14+
15+
dependencies {
16+
implementation(platform(libs.ktor.bom))
17+
implementation(libs.mcp.kotlin.server)
18+
implementation(libs.ktor.server.netty)
19+
implementation(libs.ktor.server.sse)
20+
implementation(libs.ktor.server.content.negotiation)
21+
implementation(libs.ktor.server.cors)
22+
implementation(libs.ktor.client.cio)
23+
implementation(libs.ktor.client.content.negotiation)
24+
implementation(libs.ktor.serialization.kotlinx.json)
25+
implementation(libs.kotlinx.serialization.json)
26+
implementation(libs.kotlinx.coroutines.core)
27+
implementation(libs.logback.classic)
28+
29+
testImplementation(kotlin("test"))
30+
testImplementation(libs.kotlinx.coroutines.test)
31+
testImplementation(libs.ktor.client.mock)
32+
testImplementation(libs.mcp.kotlin.client)
33+
testImplementation(libs.mcp.kotlin.testing)
34+
}
35+
36+
tasks.test {
37+
useJUnitPlatform()
38+
}
39+
40+
kotlin {
41+
jvmToolchain(21)
42+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
org.gradle.configuration-cache=true
2+
org.gradle.parallel=true
3+
org.gradle.caching=true
4+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[versions]
2+
kotlin = "2.3.20"
3+
ktor = "3.3.3"
4+
mcp-kotlin = "0.11.1"
5+
serialization = "1.10.0"
6+
coroutines = "1.10.2"
7+
logback = "1.5.32"
8+
shadow = "9.4.1"
9+
10+
[libraries]
11+
ktor-bom = { group = "io.ktor", name = "ktor-bom", version.ref = "ktor" }
12+
ktor-server-netty = { group = "io.ktor", name = "ktor-server-netty" }
13+
ktor-server-sse = { group = "io.ktor", name = "ktor-server-sse" }
14+
ktor-server-content-negotiation = { group = "io.ktor", name = "ktor-server-content-negotiation" }
15+
ktor-server-cors = { group = "io.ktor", name = "ktor-server-cors" }
16+
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio" }
17+
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation" }
18+
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json" }
19+
mcp-kotlin-server = { group = "io.modelcontextprotocol", name = "kotlin-sdk-server", version.ref = "mcp-kotlin" }
20+
mcp-kotlin-client = { group = "io.modelcontextprotocol", name = "kotlin-sdk-client", version.ref = "mcp-kotlin" }
21+
mcp-kotlin-testing = { group = "io.modelcontextprotocol", name = "kotlin-sdk-testing", version.ref = "mcp-kotlin" }
22+
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
23+
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
24+
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
25+
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
26+
ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock" }
27+
28+
[plugins]
29+
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
30+
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
31+
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
47.8 KB
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
6+
zipStoreBase=GRADLE_USER_HOME
7+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)