fix!: harden lifecycle, dispatchers, back-pressure (#778)

fixes #573 
fixes #574 
fixes #708 

- Handlers ran on `Dispatchers.IO` → now `Dispatchers.Default`,
configurable via `handlerDispatcher`.
- Reader/writer dispatcher exposed as `ioDispatcher` (default
`IODispatcher.limitedParallelism(2)`).
- Optional caller-supplied `CoroutineScope` so the pipeline isn't
detached from the caller's tree.
- `CancellationException` consistently rethrown across all pumps.
- Unbounded channels → bounded with graceful drain on close (in-flight
outbound flushed before `onClose`).
- Input `Source` now closed on natural EOF / reader error (was leaking
for non-`System.in` sources).
- Race between `start()` and `close()` fixed via 3-state CAS +
`setupComplete` barrier replacing `AtomicBoolean`.

## How Has This Been Tested?
unit and integration tests 

## Breaking Changes
- Handlers default to `Dispatchers.Default` (was `Dispatchers.IO`).
Override with `handlerDispatcher = Dispatchers.IO` if your handlers
perform blocking I/O.
- `send()` suspends under back-pressure instead of buffering unbounded.
- `StdioServerTransport(input, output)` ctor is `@Deprecated` (WARNING)
in favor of `StdioServerTransport(input, output) { ... }`. Still
binary-compatible.

## 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)
- [ ] 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:
Pavel Gorgulov 2026-06-01 12:10:01 +02:00 • committed by GitHub
commit 0adfb3bcdb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -219,11 +219,22 @@ public final class io/modelcontextprotocol/kotlin/sdk/server/SseServerTransport
public final class io/modelcontextprotocol/kotlin/sdk/server/StdioServerTransport : io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport {
public fun <init> (Lkotlinx/io/Source;Lkotlinx/io/Sink;)V
public fun <init> (Lkotlinx/io/Source;Lkotlinx/io/Sink;Lkotlin/jvm/functions/Function1;)V
public synthetic fun <init> (Lkotlinx/io/Source;Lkotlinx/io/Sink;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun send (Lio/modelcontextprotocol/kotlin/sdk/types/JSONRPCMessage;Lio/modelcontextprotocol/kotlin/sdk/shared/TransportSendOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun start (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class io/modelcontextprotocol/kotlin/sdk/server/StdioServerTransport$Builder {
public final fun getHandlerDispatcher ()Lkotlinx/coroutines/CoroutineDispatcher;
public final fun getIoDispatcher ()Lkotlinx/coroutines/CoroutineDispatcher;
public final fun getScope ()Lkotlinx/coroutines/CoroutineScope;
public final fun setHandlerDispatcher (Lkotlinx/coroutines/CoroutineDispatcher;)V
public final fun setIoDispatcher (Lkotlinx/coroutines/CoroutineDispatcher;)V
public final fun setScope (Lkotlinx/coroutines/CoroutineScope;)V
}
public final class io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransport : io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport {
public static final field STANDALONE_SSE_STREAM_ID Ljava/lang/String;
public fun <init> ()V