kotlin-sdk/kotlin-sdk-server
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Konstantin Pavlov 736efe9dc0
fix(server): notifications not delivered over Streamable HTTP (#587) (#599)
## Changes

- fix(server): keep SSE connection open until explicitly cancelled
- conformance: add logging to test_tool_with_logging and update
troubleshooting documentation
- chore(conformance): remove 5 passing server tests from conformance
baselines:
    - tools-call-with-logging
    - tools-call-with-progress
    - tools-call-sampling
    - tools-call-elicitation
    - elicitation-sep1034-defaults

## Motivation and Context

Two bugs in StreamableHttpServerTransport caused server-to-client
notifications (e.g. notifications/message during tool calls) to be
silently dropped.
   
**Bug 1 — GET SSE stream closed immediately**
`handleGetRequest` rejected GET requests with 405 when
`enableJsonResponse = true` (which `mcpStreamableHttp` always sets).
Since the Ktor `sse {}` handler commits response headers before the body
runs, the reject failed silently and the function returned, causing Ktor
to close the SSE stream. There was also no `awaitCancellation()` to keep
the connection alive.
**Bug 2 — Notifications discarded in JSON mode**

In `send()`, notifications with a relatedRequestId entered the
POST-stream path but hit `if (!isTerminated) return` without being
forwarded anywhere.
   
**Fix**
- Remove the enableJsonResponse guard from handleGetRequest. The GET SSE
stream is an always-required notification channel, orthogonal to how
POST responses are delivered.
- Add `awaitCancellation()` to keep the GET SSE connection open until
the client disconnects or the transport closes.
- In `send()`, route notifications with a relatedRequestId to the
standalone GET SSE stream when `enableJsonResponse = true`.

## How Has This Been Tested?

Verified against the MCP conformance test suite — the
ToolsCallWithLogging scenario now passes. ngrep confirms the `GET /mcp`
stream stays open and `notifications/message` events arrive before the
`tools/call` response.


## Breaking Changes
<!-- Will users need to update their code or configurations? -->

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] 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

Fixes #587
Requires #585 to be merged first.

---------

Co-authored-by: devcrocod <devcrocod@gmail.com>
2026-03-12 16:52:27 +02:00
..
api feat: add tool execution properties to Tool (#567) 2026-03-10 20:05:44 +01:00
src fix(server): notifications not delivered over Streamable HTTP (#587) (#599) 2026-03-12 16:52:27 +02:00
build.gradle.kts fix!(server): #575 Rethrow CancellationException in StdioServerTransport, fix ReadBuffer (#571) 2026-03-03 16:10:25 +02:00
detekt-baseline-commonMainSourceSet.xml feat! Add context to server call handlers (#515) 2026-02-23 16:30:15 +02:00
detekt-baseline-main.xml feat! Add context to server call handlers (#515) 2026-02-23 16:30:15 +02:00
Module.md fix!: Migrate Ktor extension functions from Routing to Route (#237) (#531) 2026-02-18 16:43:37 +02:00