Skip to content

Commit d6d460e

Browse files
committed
feat: deprecate roots sampling and logging
Warn when Roots, Sampling, or Logging APIs are used with MCP protocol version 2026-07-28, while leaving the latest stable default at 2025-11-25. Add a shared protocol deprecation helper, YARD deprecation notes, and regression tests covering warnings for 2026-07-28 and no warnings for older protocol versions. Closes #390
1 parent 23af995 commit d6d460e

18 files changed

Lines changed: 493 additions & 16 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Warn when deprecated Roots, Sampling, or Logging APIs are used with protocol version `2026-07-28` (#390)
13+
1014
## [0.20.0] - 2026-06-14
1115

1216
### Added

lib/mcp.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require_relative "json_rpc_handler"
44
require_relative "mcp/configuration"
5+
require_relative "mcp/protocol_deprecations"
56
require_relative "mcp/string_utils"
67
require_relative "mcp/transport"
78
require_relative "mcp/version"

lib/mcp/client/http.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require_relative "../../json_rpc_handler"
55
require_relative "../configuration"
66
require_relative "../methods"
7+
require_relative "../protocol_deprecations"
78
require_relative "../version"
89

910
module MCP
@@ -155,6 +156,7 @@ def connect(client_info: nil, protocol_version: nil, capabilities: {})
155156
error_type: :internal_error,
156157
)
157158
end
159+
MCP::ProtocolDeprecations.warn_for_client_capabilities(capabilities, protocol_version: negotiated_protocol_version, uplevel: 1)
158160

159161
begin
160162
send_request(request: {

lib/mcp/client/stdio.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require_relative "../../json_rpc_handler"
88
require_relative "../configuration"
99
require_relative "../methods"
10+
require_relative "../protocol_deprecations"
1011
require_relative "../version"
1112

1213
module MCP
@@ -109,6 +110,7 @@ def connect(client_info: nil, protocol_version: nil, capabilities: {})
109110
error_type: :internal_error,
110111
)
111112
end
113+
MCP::ProtocolDeprecations.warn_for_client_capabilities(capabilities, protocol_version: negotiated_protocol_version, uplevel: 1)
112114

113115
begin
114116
notification = {

lib/mcp/configuration.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
module MCP
44
class Configuration
55
LATEST_STABLE_PROTOCOL_VERSION = "2025-11-25"
6+
ROOTS_SAMPLING_LOGGING_DEPRECATED_PROTOCOL_VERSION = "2026-07-28"
67
SUPPORTED_STABLE_PROTOCOL_VERSIONS = [
7-
LATEST_STABLE_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05",
8+
ROOTS_SAMPLING_LOGGING_DEPRECATED_PROTOCOL_VERSION, LATEST_STABLE_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05",
89
]
910
DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26"
1011

lib/mcp/protocol_deprecations.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "configuration"
4+
5+
module MCP
6+
module ProtocolDeprecations
7+
extend self
8+
9+
ROOTS_MESSAGE =
10+
"MCP Roots (`roots/list` and `notifications/roots/list_changed`) is deprecated as of protocol version " \
11+
"2026-07-28 (SEP-2577). Use tool parameters, resource URIs, server configuration, or environment " \
12+
"variables instead."
13+
SAMPLING_MESSAGE =
14+
"MCP Sampling (`sampling/createMessage`) is deprecated as of protocol version 2026-07-28 (SEP-2577). " \
15+
"Use direct LLM provider APIs instead."
16+
LOGGING_MESSAGE =
17+
"MCP Logging (`logging/setLevel` and `notifications/message`) is deprecated as of protocol version " \
18+
"2026-07-28 (SEP-2577). Use stderr or OpenTelemetry instead."
19+
20+
MESSAGES = {
21+
roots: ROOTS_MESSAGE,
22+
sampling: SAMPLING_MESSAGE,
23+
logging: LOGGING_MESSAGE,
24+
}.freeze
25+
26+
def deprecated_roots_sampling_logging?(protocol_version)
27+
protocol_version == Configuration::ROOTS_SAMPLING_LOGGING_DEPRECATED_PROTOCOL_VERSION
28+
end
29+
30+
def warn_for(feature, protocol_version:, uplevel: 1)
31+
return unless deprecated_roots_sampling_logging?(protocol_version)
32+
33+
Kernel.warn(MESSAGES.fetch(feature), uplevel: uplevel + 1)
34+
end
35+
36+
def warn_for_client_capabilities(capabilities, protocol_version:, uplevel: 1)
37+
return unless deprecated_roots_sampling_logging?(protocol_version)
38+
return unless capabilities
39+
40+
warn_for(:roots, protocol_version: protocol_version, uplevel: uplevel + 1) if capability?(capabilities, :roots)
41+
warn_for(:sampling, protocol_version: protocol_version, uplevel: uplevel + 1) if capability?(capabilities, :sampling)
42+
end
43+
44+
def capability?(capabilities, key)
45+
capabilities.key?(key) || capabilities.key?(key.to_s)
46+
end
47+
private :capability?
48+
end
49+
end

lib/mcp/server.rb

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require_relative "methods"
88
require_relative "logging_message_notification"
99
require_relative "progress"
10+
require_relative "protocol_deprecations"
1011
require_relative "server_context"
1112
require_relative "server/pagination"
1213
require_relative "server/transports"
@@ -139,6 +140,7 @@ def initialize(
139140
self.page_size = page_size
140141
@configuration = MCP.configuration.merge(configuration)
141142
@client = nil
143+
@client_protocol_version = nil
142144

143145
validate!
144146

@@ -250,10 +252,15 @@ def notify_resources_list_changed
250252
report_exception(e, { notification: "resources_list_changed" })
251253
end
252254

255+
# @deprecated MCP Logging (`logging/setLevel` and `notifications/message`)
256+
# is deprecated as of MCP protocol version 2026-07-28 (SEP-2577).
257+
# Use stderr or OpenTelemetry instead.
253258
def notify_log_message(data:, level:, logger: nil)
254259
return unless @transport
255260
return unless logging_message_notification&.should_notify?(level)
256261

262+
warn_if_deprecated_protocol_feature(:logging, uplevel: 1)
263+
257264
params = { "data" => data, "level" => level }
258265
params["logger"] = logger if logger
259266

@@ -266,7 +273,13 @@ def notify_log_message(data:, level:, logger: nil)
266273
# Called when a client notifies the server that its filesystem roots have changed.
267274
#
268275
# @yield [params] The notification params (typically `nil`).
276+
# @deprecated MCP Roots (`roots/list` and
277+
# `notifications/roots/list_changed`) is deprecated as of MCP protocol
278+
# version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs,
279+
# server configuration, or environment variables instead.
269280
def roots_list_changed_handler(&block)
281+
warn_if_deprecated_protocol_feature(:roots, uplevel: 1)
282+
270283
@handlers[Methods::NOTIFICATIONS_ROOTS_LIST_CHANGED] = block
271284
end
272285

@@ -448,6 +461,8 @@ def handle_request(request, method, session: nil, related_request_id: nil)
448461
server_context: { request: request },
449462
exception_already_reported: ->(e) { reported_exception.equal?(e) },
450463
) do
464+
warn_if_deprecated_protocol_feature(:roots, session: session, uplevel: 0) if method == Methods::NOTIFICATIONS_ROOTS_LIST_CHANGED
465+
451466
result = case method
452467
when Methods::INITIALIZE
453468
init(params, session: session)
@@ -562,7 +577,11 @@ def init(params, session: nil)
562577
response_instructions = nil
563578
end
564579

565-
session&.mark_initialized!
580+
if session
581+
session.mark_initialized!(protocol_version: negotiated_version)
582+
else
583+
@client_protocol_version = negotiated_version
584+
end
566585

567586
{
568587
protocolVersion: negotiated_version,
@@ -577,6 +596,8 @@ def configure_logging_level(request, session: nil)
577596
raise RequestHandlerError.new("Server does not support logging", request, error_type: :internal_error)
578597
end
579598

599+
warn_if_deprecated_protocol_feature(:logging, session: session, uplevel: 0)
600+
580601
logging_message_notification = LoggingMessageNotification.new(level: request[:level])
581602
unless logging_message_notification.valid_level?
582603
raise RequestHandlerError.new("Invalid log level #{request[:level]}", request, error_type: :invalid_params)
@@ -588,6 +609,19 @@ def configure_logging_level(request, session: nil)
588609
{}
589610
end
590611

612+
def warn_if_deprecated_protocol_feature(feature, session: nil, uplevel: 1)
613+
protocol_version = effective_deprecation_protocol_version(session)
614+
MCP::ProtocolDeprecations.warn_for(feature, protocol_version: protocol_version, uplevel: uplevel + 1)
615+
end
616+
617+
def effective_deprecation_protocol_version(session)
618+
session&.protocol_version || @client_protocol_version || explicit_protocol_version
619+
end
620+
621+
def explicit_protocol_version
622+
configuration.protocol_version if configuration.protocol_version?
623+
end
624+
591625
def list_tools(request)
592626
page = paginate(@tools.values, cursor: cursor_from(request), page_size: @page_size, request: request, &:to_h)
593627

lib/mcp/server_context.rb

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,19 @@ def report_progress(progress, total: nil, message: nil)
3535
# @param data [Object] The log data to send.
3636
# @param level [String] Log level (e.g., `"debug"`, `"info"`, `"error"`).
3737
# @param logger [String, nil] Logger name.
38+
# @deprecated MCP Logging (`logging/setLevel` and `notifications/message`)
39+
# is deprecated as of MCP protocol version 2026-07-28 (SEP-2577).
40+
# Use stderr or OpenTelemetry instead.
3841
def notify_log_message(data:, level:, logger: nil)
3942
return unless @notification_target
4043

41-
@notification_target.notify_log_message(data: data, level: level, logger: logger, related_request_id: @related_request_id)
44+
@notification_target.notify_log_message(
45+
data: data,
46+
level: level,
47+
logger: logger,
48+
related_request_id: @related_request_id,
49+
**server_session_deprecation_options,
50+
)
4251
end
4352

4453
# Sends a resource updated notification scoped to the originating session.
@@ -51,9 +60,13 @@ def notify_resources_updated(uri:)
5160
end
5261

5362
# Delegates to the session so the request is scoped to the originating client.
63+
# @deprecated MCP Roots (`roots/list` and
64+
# `notifications/roots/list_changed`) is deprecated as of MCP protocol
65+
# version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs,
66+
# server configuration, or environment variables instead.
5467
def list_roots
5568
if @notification_target.respond_to?(:list_roots)
56-
@notification_target.list_roots(related_request_id: @related_request_id)
69+
@notification_target.list_roots(related_request_id: @related_request_id, **server_session_deprecation_options)
5770
else
5871
raise NoMethodError, "undefined method 'list_roots' for #{self}"
5972
end
@@ -84,9 +97,12 @@ def ping
8497
# Delegates to the session so the request is scoped to the originating client.
8598
# Falls back to `@context` (via `method_missing`) when `@notification_target`
8699
# does not support sampling.
100+
# @deprecated MCP Sampling (`sampling/createMessage`) is deprecated as of
101+
# MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider
102+
# APIs instead.
87103
def create_sampling_message(**kwargs)
88104
if @notification_target.respond_to?(:create_sampling_message)
89-
@notification_target.create_sampling_message(**kwargs, related_request_id: @related_request_id)
105+
@notification_target.create_sampling_message(**kwargs, related_request_id: @related_request_id, **server_session_deprecation_options)
90106
elsif @context.respond_to?(:create_sampling_message)
91107
@context.create_sampling_message(**kwargs, related_request_id: @related_request_id)
92108
else
@@ -141,5 +157,11 @@ def method_missing(name, ...)
141157
def respond_to_missing?(name, include_private = false)
142158
@context.respond_to?(name) || super
143159
end
160+
161+
private
162+
163+
def server_session_deprecation_options
164+
@notification_target.is_a?(ServerSession) ? { deprecation_uplevel: 2 } : {}
165+
end
144166
end
145167
end

lib/mcp/server_session.rb

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module MCP
77
# Holds per-connection state for a single client session.
88
# Created by the transport layer; delegates request handling to the shared `Server`.
99
class ServerSession
10-
attr_reader :session_id, :client, :logging_message_notification
10+
attr_reader :session_id, :client, :logging_message_notification, :protocol_version
1111

1212
def initialize(server:, transport:, session_id: nil)
1313
@server = server
@@ -16,6 +16,7 @@ def initialize(server:, transport:, session_id: nil)
1616
@client = nil
1717
@client_capabilities = nil
1818
@logging_message_notification = nil
19+
@protocol_version = nil
1920
@in_flight = {}
2021
@in_flight_mutex = Mutex.new
2122
@initialized = false
@@ -29,8 +30,9 @@ def initialized?
2930
# Called by `Server#init` after a successful `initialize` response, so subsequent
3031
# `initialize` requests on the same session can be rejected per MCP spec
3132
# (the initialization phase MUST be the first interaction).
32-
def mark_initialized!
33+
def mark_initialized!(protocol_version: nil)
3334
@initialized = true
35+
@protocol_version = protocol_version
3436
end
3537

3638
# Registers a `Cancellation` token for an in-flight request.
@@ -98,11 +100,17 @@ def client_capabilities
98100
end
99101

100102
# Sends a `roots/list` request scoped to this session.
101-
def list_roots(related_request_id: nil)
103+
# @deprecated MCP Roots (`roots/list` and
104+
# `notifications/roots/list_changed`) is deprecated as of MCP protocol
105+
# version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs,
106+
# server configuration, or environment variables instead.
107+
def list_roots(related_request_id: nil, deprecation_uplevel: 1)
102108
unless client_capabilities&.dig(:roots)
103109
raise "Client does not support roots."
104110
end
105111

112+
@server.send(:warn_if_deprecated_protocol_feature, :roots, session: self, uplevel: deprecation_uplevel)
113+
106114
send_to_transport_request(Methods::ROOTS_LIST, nil, related_request_id: related_request_id)
107115
end
108116

@@ -115,8 +123,13 @@ def ping(related_request_id: nil)
115123
end
116124

117125
# Sends a `sampling/createMessage` request scoped to this session.
118-
def create_sampling_message(related_request_id: nil, **kwargs)
126+
# @deprecated MCP Sampling (`sampling/createMessage`) is deprecated as of
127+
# MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider
128+
# APIs instead.
129+
def create_sampling_message(related_request_id: nil, deprecation_uplevel: 1, **kwargs)
119130
params = @server.build_sampling_params(client_capabilities, **kwargs)
131+
@server.send(:warn_if_deprecated_protocol_feature, :sampling, session: self, uplevel: deprecation_uplevel)
132+
120133
send_to_transport_request(Methods::SAMPLING_CREATE_MESSAGE, params, related_request_id: related_request_id)
121134
end
122135

@@ -188,10 +201,15 @@ def notify_progress(progress_token:, progress:, total: nil, message: nil, relate
188201
end
189202

190203
# Sends a log message notification to this session only.
191-
def notify_log_message(data:, level:, logger: nil, related_request_id: nil)
204+
# @deprecated MCP Logging (`logging/setLevel` and `notifications/message`)
205+
# is deprecated as of MCP protocol version 2026-07-28 (SEP-2577).
206+
# Use stderr or OpenTelemetry instead.
207+
def notify_log_message(data:, level:, logger: nil, related_request_id: nil, deprecation_uplevel: 1)
192208
effective_logging = @logging_message_notification || @server.logging_message_notification
193209
return unless effective_logging&.should_notify?(level)
194210

211+
@server.send(:warn_if_deprecated_protocol_feature, :logging, session: self, uplevel: deprecation_uplevel)
212+
195213
params = { "data" => data, "level" => level }
196214
params["logger"] = logger if logger
197215

0 commit comments

Comments
 (0)