Skip to content

feat! Add context to server call handlers#515

Open
rnett wants to merge 22 commits intomodelcontextprotocol:mainfrom
rnett:rnett/add-server-context
Open

feat! Add context to server call handlers#515
rnett wants to merge 22 commits intomodelcontextprotocol:mainfrom
rnett:rnett/add-server-context

Conversation

@rnett
Copy link

@rnett rnett commented Feb 12, 2026

Motivation and Context

Adds a ClientConnection context receiver to server call handlers, which provides access to the client communication channel. This is necessary to allow tools to send notifications, logging, elicitations, etc.

Supersedes #144, discussed in #295.

How Has This Been Tested?

With the added integration test.

Breaking Changes

It breaks binary compatibility and breaks source compatibility for tool handlers that already use this.

If you care a lot about maintaining compatibility, I could add some overloads to ease the transition. But the new methods would likely need to be named differently for resolution.

Future compatibility should be easier to maintain - Context is an interface (with experimental opt-in subclassing), so new fields can be added without breaking compatibility. Different types could also be introduced for different handles without breaking compatibility, as long as they extend ClientConnection.

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

Additional context

Copilot AI review requested due to automatic review settings February 12, 2026 01:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a Context receiver to server request handlers so tool/prompt/resource implementations can access the active ServerSession (e.g., to send notifications).

Changes:

  • Introduced Context + Handler<T, R> (suspend Context.(T) -> R) to provide session-aware handler APIs
  • Updated server dispatch to pass session-backed context through tool/prompt/resource handler execution
  • Added an integration test verifying a tool can send a notification via Context.session

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

File Description
kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt Threads ServerSession into handler execution and updates public registration APIs to use Handler
kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Feature.kt Introduces Handler typealias and updates registered feature handler types
kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Context.kt Adds new public Context interface backed by internal ContextImpl(session)
integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolNotificationTest.kt Validates tools can send notifications using Context.session

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 603 to 605
return prompt.run {
ContextImpl(session).messageProvider(request)
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The new context-receiver behavior is exercised for tools via the added integration test, but prompts/resources now also rely on ContextImpl(session) for execution. Add integration (or unit) tests covering at least one prompt and one resource handler using session from Context (e.g., sending a notification/log message) to ensure the new receiver wiring works consistently across all handler types.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@kpavlov kpavlov left a comment

Choose a reason for hiding this comment

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

This change makes perfect sense, and your integration test really shows it off! 👍🏻

However, merging it as it is could cause some issues.

  1. Let's add overloads as context is not always required and to avoid breaking compatibility.
  2. Also, let’s include integration tests for all types of affecter handlers.

Copy link
Contributor

@devcrocod devcrocod left a comment

Choose a reason for hiding this comment

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

At the moment, I don’t quite see the need for a Context and just store session, if we only want to expose a session. We could achieve the same without requiring session.someFun() to be written every time

I’ll also try to find out whether it’s possible to overload functions by lambda. in the past, Kotlin had a special annotation for this. That could help us preserve compatibility.

I’m also concerned about the naming. Since this will be part of the public API, it would be better to use more specific names. Besides potential conflicts, this could also confuse users, as code completion will frequently suggest these symbols

cc: @tiginamaria

@kpavlov
Copy link
Contributor

kpavlov commented Feb 12, 2026

I do not agree, that it is a kind of useless api. This is at least the second community PR: #144

Context is pretty common pattern for server implementation and it provides is a good extension point, also could be used as a place to store some user attributes...
In software, API is never set in stone, unless it is sealed on a chip. We can annotate it @ExperimentalMcpApi.

@rnett
Copy link
Author

rnett commented Feb 12, 2026

FWIW, the reason I went with Context over just session is that it allows for future changes and extensibility without breaking either source or binary compatibility. Using session would for the current objectives, but if something changed in the future, it could easily cause another breaking change.

@rnett
Copy link
Author

rnett commented Feb 12, 2026

RE overloads (@kpavlov), do you mean for the registerTool, etc., methods? Because while agree it would be nice, it's not possible for Kotlin to resolve the overloads for a call like:

addTool(tool) {
  // is there a Context receiver or not? - the compiler reports an overload resolution error
}

The only alternative I could think of is using a different name for the new methods, like register instead of add. Thoughts?

@devcrocod
Copy link
Contributor

FWIW, the reason I went with Context over just session is that it allows for future changes and extensibility without breaking either source or binary compatibility. Using session would for the current objectives, but if something changed in the future, it could easily cause another breaking change.

Yes, that makes sense. What I meant was to attach extensions to the context in this case, so that the calls are made without a session. Since in the context we have only one variable, all calls will go through the session anyway, so specifying it every time is redundant

@devcrocod
Copy link
Contributor

RE overloads (@kpavlov), do you mean for the registerTool, etc., methods? Because while agree it would be nice, it's not possible for Kotlin to resolve the overloads for a call like:

I checked and it looks like there’s only an annotation for overload resolution by return type @OverloadResolutionByLambdaReturnType.
Unfortunately, that won’t work in this case

Copilot AI review requested due to automatic review settings February 14, 2026 22:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 14, 2026 22:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@rnett
Copy link
Author

rnett commented Feb 14, 2026

Yes, that makes sense. What I meant was to attach extensions to the context in this case, so that the calls are made without a session. Since in the context we have only one variable, all calls will go through the session anyway, so specifying it every time is redundant

Ah I see. Personally, I don't find it that verbose, but I've updated the context so that you can call the methods directly on it.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 17, 2026 08:25
@kpavlov kpavlov force-pushed the rnett/add-server-context branch from 164c935 to dbbe95b Compare February 17, 2026 08:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@kpavlov kpavlov added the enhancement New feature or request label Feb 17, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 19, 2026 04:59
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt:306

  • The public handler APIs now expose ClientConnection (which is annotated @ExperimentalMcpApi) in their signatures, but Server/addTool/addPrompt/addResource themselves are only annotated with @OptIn at the class level. If the intent is for these new context-receiver handler APIs to be experimental for callers, these public methods should be annotated with @ExperimentalMcpApi (or ClientConnection should be de-experimentalized) so the opt-in requirement is clearly propagated to downstream users.
    /**
     * Registers a single tool. The client can then call this tool.
     *
     * @param tool A [Tool] object describing the tool.
     * @param handler A suspend function that handles executing the tool when called by the client.
     * @throws IllegalStateException If the server does not support tools.
     */
    public fun addTool(tool: Tool, handler: suspend ClientConnection.(CallToolRequest) -> CallToolResult) {
        check(options.capabilities.tools != null) {
            logger.error { "Failed to add tool '${tool.name}': Server does not support tools capability" }
            "Server does not support tools capability. Enable it in ServerOptions."
        }

        toolRegistry.add(RegisteredTool(tool, handler))
    }

integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerHandlerNotificationTest.kt:54

  • These inline comments refer to using the "session" from context, but the new receiver provided to handlers is ClientConnection (and does not directly expose the session). Consider updating the wording to avoid confusion for future readers of the test.
        server.addTool(toolName, "A tool that notifies") { _ ->
            // Use the session from context to send a notification
            sendLoggingMessage(
                LoggingMessageNotification(

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@kpavlov kpavlov left a comment

Choose a reason for hiding this comment

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

Thank you, @rnett
Looks good, but I have couple of observations.

  1. At Copilot mentioned, marking ClientConnection as experimental api requires OptIn for all addXxxHandler calls. This change will be disruptive for users and we do want to keep the current api shape, so it's not really experimental for the users. Let's remove ExperimentalMcpApi from ClientConnection

  2. Maybe lets make ClientConnection an interface and create ClientConnectionImpl as internal class just for the sake of Dependency Inversion Principle, to have more flexibility in the future.

Copilot AI review requested due to automatic review settings February 19, 2026 05:38
@rnett rnett requested review from devcrocod and kpavlov February 19, 2026 05:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

// Start the ServerSession redirection section
// Start the ServerSession / ClientConnection redirection section
Copy link
Contributor

Choose a reason for hiding this comment

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

probably

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we expose it to the handler?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we expose it to the handler?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we expose it to the handler?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we expose such low-level API to the handler?

Copy link
Contributor

@kpavlov kpavlov left a comment

Choose a reason for hiding this comment

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

I removed some methods from ClientConnection.kt, updated tests

but overall LGTM.
@devcrocod , WDYT

@kpavlov kpavlov self-requested a review February 19, 2026 07:19
…ty from `ClientConnection`

- Removed `serverCapabilities` and `clientCapabilities` properties, along with redundant `onClose`, `request`, and logging-level handling methods.
- Simplified `request` and `notification` methods for improved clarity.
- Updated corresponding tests to reflect the removed functionality.
…ing for notifications

- Added tests to ensure `sendLoggingMessage` and notifications respect the set logging level and properly filter messages below the threshold.
- Updated assertions to use `shouldBe` and collection matchers for clarity.
…ication handling in tests

- Updated `ClientConnection` to simplify methods by consolidating parameters and removing unused logic.
- Replaced `Notification` with `ServerNotification` for stronger typing.
- Removed client version and message acceptance logic from the interface.
- Simplified test cases to use `shouldBe` and removed redundant assertions for improved clarity and maintainability.
Copilot AI review requested due to automatic review settings February 19, 2026 09:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@kpavlov kpavlov changed the title Add context to server call handlers feat! Add context to server call handlers Feb 19, 2026
@kpavlov kpavlov added the breaking Breaking changes 👧🏠🔥 label Feb 19, 2026
…tekt baselines, and enhance `AbstractServerFeaturesTest`

- Made `clientConnection` and `serverCapabilities` private to restrict access scope.
- Simplified detekt baselines by removing outdated or unneeded configurations.
- Enhanced `AbstractServerFeaturesTest` by adding `addTool` helper for streamlined test setup.
Copy link
Contributor

@kpavlov kpavlov left a comment

Choose a reason for hiding this comment

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

LGTM

@kpavlov kpavlov requested a review from e5l February 19, 2026 10:19
Copy link
Contributor

@devcrocod devcrocod left a comment

Choose a reason for hiding this comment

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

let’s merge it

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

Labels

breaking Breaking changes 👧🏠🔥 enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments