# Migrate Ktor extension functions from `Routing` to `Route` (#237) - Replaced `Routing` with `Route` in MCP-related functions for better composability in nested contexts. - Updated tests to verify compatibility with nested routes and subpaths. - Deprecated legacy signatures and removed redundant overloads. ## How Has This Been Tested? Unit test ## Breaking Changes `Routing.mcp(...) `extension was replaced with `Route.mcp(...)` ## Types of changes - [X] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context Based on #314 by @IainHull but that PR is too outdated.
125 lines
5.2 KiB
Markdown
125 lines
5.2 KiB
Markdown
# Module kotlin-sdk-server
|
|
|
|
`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.
|
|
|
|
## What the module provides
|
|
|
|
- **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 (`Route.mcp`, `Route.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.
|
|
|
|
## 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.
|