Repository navigation
Leave listChanged out of initialize when notifications cannot be delivered - #620
Open
koic wants to merge 1 commit into
Conversation
…vered ## Motivation and Context `initialize` answers with the server's capabilities as configured, by default `listChanged: true` on tools, prompts and resources, whatever the transport can deliver. Stateless Streamable HTTP has no session streams, so `send_notification` reports non-delivery there and a `notifications/*/list_changed` never reaches a handshake client, yet `initialize` promised it (issue modelcontextprotocol#619). The same server already leaves the flags out of `server/discover` when `subscriptions/listen` is not served, so the two lifecycles described it differently. The specification defines `listChanged` as whether the server will emit the notification when the list changes. `Transport#delivers_notifications?` now tells the server whether notifications reach a client outside a response (`true` by default, `false` for stateless Streamable HTTP), and `initialize` leaves `listChanged` and `subscribe` out when they do not, with the stripping `server/discover` already applies. The capability objects themselves stay: `prompts` and `resources` with nothing registered still answer their list methods, as the Python SDK advertises them too, and `logging` stays on both lifecycles, since `logging/setLevel` is served, although stateless Streamable HTTP drops a handshake client's log messages, as its documentation says. Each lifecycle keeps the flags its own delivery path can honor, so the two still differ where those paths do: a stateless transport serving `subscriptions/listen` keeps them in `server/discover`, since listen streams deliver the notifications there, but not in `initialize`, and a stateful one without it does the reverse. Fixes modelcontextprotocol#619. ## How Has This Been Tested? `test/mcp/server_test.rb` (the flags are stripped when the transport delivers no notifications, and kept when it does or does not say) and `test/mcp/server/transports/streamable_http_transport_test.rb` (a stateless transport's `initialize` advertises no flags and, without `subscriptions/listen`, the same capabilities as its `server/discover`; a stateful one keeps them). Against the previous library the stripping tests fail on the present flags. The same files also cover a `Capabilities` builder, string keys, the configured capabilities staying unchanged, and the two configurations where only one lifecycle keeps the flags. ## Breaking Changes The `initialize` result of a stateless Streamable HTTP server carries no `listChanged` or `subscribe` flags.
atesgoral
approved these changes
Oct 17, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Context
initializeanswers with the server's capabilities as configured, by defaultlistChanged: trueon tools, prompts and resources, whatever the transport can deliver. Stateless Streamable HTTP has no session streams, sosend_notificationreports non-delivery there and anotifications/*/list_changednever reaches a handshake client, yetinitializepromised it (issue #619). The same server already leaves the flags out ofserver/discoverwhensubscriptions/listenis not served, so the two lifecycles described it differently. The specification defineslistChangedas whether the server will emit the notification when the list changes.Transport#delivers_notifications?now tells the server whether notifications reach a client outside a response (trueby default,falsefor stateless Streamable HTTP), andinitializeleaveslistChangedandsubscribeout when they do not, with the strippingserver/discoveralready applies. The capability objects themselves stay:promptsandresourceswith nothing registered still answer their list methods, as the Python SDK advertises them too, andloggingstays on both lifecycles, sincelogging/setLevelis served, although stateless Streamable HTTP drops a handshake client's log messages, as its documentation says.Each lifecycle keeps the flags its own delivery path can honor, so the two still differ where those paths do: a stateless transport serving
subscriptions/listenkeeps them inserver/discover, since listen streams deliver the notifications there, but not ininitialize, and a stateful one without it does the reverse.Fixes #619.
How Has This Been Tested?
test/mcp/server_test.rb(the flags are stripped when the transport delivers no notifications, and kept when it does or does not say) andtest/mcp/server/transports/streamable_http_transport_test.rb(a stateless transport'sinitializeadvertises no flags and, withoutsubscriptions/listen, the same capabilities as itsserver/discover; a stateful one keeps them). Against the previous library the stripping tests fail on the present flags.The same files also cover a
Capabilitiesbuilder, string keys, the configured capabilities staying unchanged, and the two configurations where only one lifecycle keeps the flags.Breaking Changes
The
initializeresult of a stateless Streamable HTTP server carries nolistChangedorsubscribeflags.Types of changes
Checklist