Skip to content

fix(core): stop reporting cancellation as an error - #998

Merged
devcrocod merged 2 commits into
mainfrom
fix/cancellation-not-reported-as-error
Sep 22, 2026
Merged

devcrocod merged 2 commits into
mainfrom
fix/cancellation-not-reported-as-error

Conversation

@devcrocod

Copy link
Copy Markdown
Contributor

CancellationException no longer reaches onError callbacks or the log.

Breaking Changes

None

Types of changes

  • 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

  • I have read the MCP Documentation
  • 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

…Cancellable` to prevent suppressed cancellations.
Copilot AI lite review requested due to automatic review settings September 17, 2026 10:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The cancellation test does not compile because it passes a suspend function to a non-suspending helper.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Updates MCP transport and protocol handling so CancellationException is propagated without being logged or reported as an error.

Changes:

  • Adds cancellation-aware exception handling and tests.
  • Updates client, server, WebSocket, and protocol cleanup paths.
  • Records the new protected API.
File summaries
File Summary
kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransport.kt Filters cancellation during stream cleanup.
kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/utils/RunCatchingCancellableTest.kt Tests cancellation-aware exception handling.
kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/utils/RunCatchingCancellable.kt Adds cancellation-preserving result handling.
kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/WebSocketMcpTransport.kt Treats cancellation as normal closure.
kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Protocol.kt Suppresses secondary cancellation failures.
kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt Adds safe error-callback invocation.
kotlin-sdk-core/api/kotlin-sdk-core.api Records the protected API addition.
kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransport.kt Uses cancellation-aware callbacks.
kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SseClientTransport.kt Preserves cancellation during setup and cleanup.
kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt Stops logging initialization cancellation.
Review details

Suppressed comments (1)

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/WebSocketMcpTransport.kt:89

  • This changes the WebSocket lifecycle contract, but the added test only exercises runCatchingCancellable; there is no WebSocket transport test asserting that a cancelled session invokes onClose without onError. Add a regression test for this branch, since it is the behavior changed here and the existing WebSocket coverage only exercises successful connections.
            if (it != null && it !is CancellationException) {
  • Files reviewed: 10/10 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@devcrocod
devcrocod requested a review from e5l September 17, 2026 11:04

@e5l e5l left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copilot AI review requested due to automatic review settings September 22, 2026 10:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

One or more issues must be addressed before approval.

Review effort: Lite
Findings: None

Resolved since last review (1)

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, careful fix — cancellation handling looks right and consistent with the rest of the transports, so this is good to merge.


Produced by Air Automations. Name: Code review / Run: https://air.jetbrains.cloud/org/05cf1a7f-6ab5-713b-abd3-29d0c8a05e2d/automations/409567ec-d3a7-40ac-ba95-8518ec577ac2?run=d4bbb085-17bd-4430-85e3-d5f2767fea4c

@devcrocod
devcrocod merged commit 7df3af2 into main Sep 22, 2026
24 of 25 checks passed
@devcrocod
devcrocod deleted the fix/cancellation-not-reported-as-error branch September 22, 2026 11:01
protected fun invokeOnCloseCallback() {
if (onCloseCalled.compareAndSet(expectedValue = false, newValue = true)) {
runCatching { _onClose() }
runCatchingCancellable { _onClose() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale statement — kotlin-sdk-core/.../shared/AbstractTransport.kt:66-67, in the KDoc of invokeOnCloseCallback:

Any exceptions thrown during the execution of the _onClose callback are caught and suppressed.

What falsified it — this PR swapped runCatching { _onClose() } for runCatchingCancellable { _onClose() } on line 71. runCatchingCancellable re-throws CancellationException instead of capturing it, so invokeOnCloseCallback can now throw, and "any exceptions ... are caught and suppressed" is no longer true. The sibling invokeOnErrorCallback added in the same commit documents exactly this nuance; invokeOnCloseCallback was left with the pre-change wording.

Suggested correction — replace that sentence with wording that matches the new behaviour, e.g.: "Any [Throwable] the _onClose callback raises is caught and suppressed. A [kotlin.coroutines.cancellation.CancellationException] propagates instead."


Produced by Air Automations. Name: Documentation maintenance / Run: https://air.jetbrains.cloud/org/05cf1a7f-6ab5-713b-abd3-29d0c8a05e2d/automations/78771d31-9b0b-450c-810f-118fb9430a87?run=5c807b4e-2447-499a-a7ce-a6c26a496e43

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants