Users currently must manually `install(ContentNegotiation) {
json(McpJson) }` before calling `mcp()`,
`mcpStreamableHttp()`, or `mcpStatelessStreamableHttp()`. Forgetting
this or using the default `json()`
instead of `json(McpJson)` causes silent serialization failures at
runtime (e.g., explicit null fields in JSON-RPC
responses). This PR makes the SDK install it automatically.
Fixes #664
## Changes
- **Core feature** — `mcp()`, `mcpStreamableHttp()`, and
`mcpStatelessStreamableHttp()` now auto-install `ContentNegotiation`
configured with `McpJson`. If the user already installed
`ContentNegotiation`, a warning is logged instead of crashing with
`DuplicatePluginException`.
- **`KtorServerHelpers.kt`** (new) — `installMcpContentNegotiation()`
internal helper with `pluginOrNull` guard and warning
- **`KtorServer.kt`** — calls the helper from all three entry points
- **`build.gradle.kts`** — adds `ktor-server-content-negotiation` and
`ktor-serialization` as `implementation` deps to `kotlin-sdk-server`
- **Removed manual installs** from `ConformanceServer.kt` and
`AbstractStreamableHttpIntegrationTest.kt`.
- **Test infrastructure** — replaced `slf4j-simple` with Logback and use
`ListAppender` (`LogCapture` utility) for deterministic log assertion.
Added `logback-test.xml` matching previous log levels.
- **`StreamableHttpServerConfigurationTest.kt`** (new) — verifies the
warning for all three entry points: `mcpStreamableHttp`,
`mcpStatelessStreamableHttp`, and `mcp` (SSE).
- **README.md** — removed manual `ContentNegotiation` boilerplate from
examples, documented automatic installation, added CORS configuration
snippet for browser-based clients.
- **CI** — added `ktlintCheck` to the Linux build matrix job.
## How Has This Been Tested?
Unit/integration tests. Conformance tests updated
## Breaking Changes
No
## Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] 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
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
This commit is contained in:
parent
e21d2298a1
commit
3617ce4e15
1 changed files with 267 additions and 61 deletions
87
README.md
87
README.md
|
|
@ -19,34 +19,34 @@ standardized protocol interface.
|
|||
|
||||
* [Overview](#overview)
|
||||
* [Installation](#installation)
|
||||
* [Artifacts](#artifacts)
|
||||
* [Gradle setup (JVM)](#gradle-setup-jvm)
|
||||
* [Multiplatform](#multiplatform)
|
||||
* [Ktor dependencies](#ktor-dependencies)
|
||||
* [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)
|
||||
* [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)
|
||||
* [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)
|
||||
* [ChannelTransport (testing)](#channeltransport-testing)
|
||||
* [STDIO Transport](#stdio-transport)
|
||||
* [Streamable HTTP Transport](#streamable-http-transport)
|
||||
* [SSE Transport](#sse-transport)
|
||||
* [WebSocket Transport](#websocket-transport)
|
||||
* [ChannelTransport (testing)](#channeltransport-testing)
|
||||
* [Connecting your server](#connecting-your-server)
|
||||
* [Examples](#examples)
|
||||
* [Documentation](#documentation)
|
||||
|
|
@ -188,24 +188,19 @@ the [simple-streamable-server](samples/simple-streamable-server) sample.
|
|||
<!--- CLEAR -->
|
||||
|
||||
```kotlin
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.Server
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.mcpStreamableHttp
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.McpJson
|
||||
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(
|
||||
|
|
@ -231,11 +226,8 @@ fun main(args: Array<String>) {
|
|||
) { request ->
|
||||
CallToolResult(content = listOf(TextContent("Hello, world!")))
|
||||
}
|
||||
|
||||
|
||||
embeddedServer(CIO, host = "127.0.0.1", port = port) {
|
||||
install(ContentNegotiation) {
|
||||
json(McpJson)
|
||||
}
|
||||
mcpStreamableHttp {
|
||||
mcpServer
|
||||
}
|
||||
|
|
@ -774,8 +766,10 @@ CLI tooling that spawns a helper process. No networking setup is required.
|
|||
|
||||
`StreamableHttpClientTransport` and the Ktor `mcpStreamableHttp()` / `mcpStatelessStreamableHttp()` 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. Both accept a `path` parameter (default:
|
||||
`"/mcp"`) to mount the endpoint at any URL:
|
||||
remote deployments and integrates nicely with proxies or service meshes.
|
||||
|
||||
These helpers automatically install `ContentNegotiation` with `McpJson` — do not install it yourself, or a warning will
|
||||
be logged. Both accept a `path` parameter (default: `"/mcp"`) to mount the endpoint at any URL:
|
||||
|
||||
<!--- CLEAR -->
|
||||
<!--- INCLUDE
|
||||
|
|
@ -809,11 +803,30 @@ embeddedServer(CIO, port = 3000) {
|
|||
-->
|
||||
<!--- KNIT example-server-routes-01.kt -->
|
||||
|
||||
**CORS for browser-based clients (e.g. MCP Inspector):** if you connect from a browser-based
|
||||
client you need to install the Ktor CORS plugin so that MCP-specific headers are allowed and exposed:
|
||||
|
||||
```kotlin
|
||||
install(CORS) {
|
||||
anyHost() // restrict to specific origins in production
|
||||
allowMethod(HttpMethod.Options)
|
||||
allowMethod(HttpMethod.Get)
|
||||
allowMethod(HttpMethod.Post)
|
||||
allowMethod(HttpMethod.Delete)
|
||||
allowNonSimpleContentTypes = true
|
||||
allowHeader("Mcp-Session-Id")
|
||||
allowHeader("Mcp-Protocol-Version")
|
||||
exposeHeader("Mcp-Session-Id")
|
||||
exposeHeader("Mcp-Protocol-Version")
|
||||
}
|
||||
```
|
||||
|
||||
### SSE Transport
|
||||
|
||||
Server-Sent Events remain available for backwards compatibility with older MCP clients. Two Ktor helpers are provided:
|
||||
|
||||
- **`Application.mcp { }`** — installs the SSE plugin automatically and registers MCP endpoints at `/`.
|
||||
- **`Application.mcp { }`** — installs SSE and `ContentNegotiation` with `McpJson` automatically, then registers MCP
|
||||
endpoints at `/`. Do not install `ContentNegotiation` yourself — the SDK handles it.
|
||||
- **`Route.mcp { }`** — registers MCP endpoints at the current route path; requires `install(SSE)` in the application
|
||||
first. Use this to host MCP alongside other routes or under a path prefix:
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue