Skip to content

Commit 74db44c

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 2f24cdf commit 74db44c

17 files changed

Lines changed: 511 additions & 6 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: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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)
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) if capability?(capabilities, :roots)
41+
warn_for(:sampling, protocol_version: protocol_version, uplevel: uplevel) if capability?(capabilities, :sampling)
42+
end
43+
44+
private
45+
46+
def capability?(capabilities, key)
47+
capabilities.key?(key) || capabilities.key?(key.to_s)
48+
end
49+
end
50+
end

lib/mcp/server.rb

Lines changed: 38 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/capabilities"
1213
require_relative "server/pagination"
@@ -140,6 +141,7 @@ def initialize(
140141
self.page_size = page_size
141142
@configuration = MCP.configuration.merge(configuration)
142143
@client = nil
144+
@client_protocol_version = nil
143145

144146
validate!
145147

@@ -256,7 +258,12 @@ def notify_resources_list_changed
256258
report_exception(e, { notification: "resources_list_changed" })
257259
end
258260

261+
# @deprecated MCP Logging (`logging/setLevel` and `notifications/message`)
262+
# is deprecated as of MCP protocol version 2026-07-28 (SEP-2577).
263+
# Use stderr or OpenTelemetry instead.
259264
def notify_log_message(data:, level:, logger: nil)
265+
warn_if_deprecated_protocol_feature(:logging, uplevel: 1)
266+
260267
return unless @transport
261268
return unless logging_message_notification&.should_notify?(level)
262269

@@ -272,7 +279,13 @@ def notify_log_message(data:, level:, logger: nil)
272279
# Called when a client notifies the server that its filesystem roots have changed.
273280
#
274281
# @yield [params] The notification params (typically `nil`).
282+
# @deprecated MCP Roots (`roots/list` and
283+
# `notifications/roots/list_changed`) is deprecated as of MCP protocol
284+
# version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs,
285+
# server configuration, or environment variables instead.
275286
def roots_list_changed_handler(&block)
287+
warn_if_deprecated_protocol_feature(:roots, uplevel: 1)
288+
276289
@handlers[Methods::NOTIFICATIONS_ROOTS_LIST_CHANGED] = block
277290
end
278291

@@ -430,6 +443,13 @@ def handle_request(request, method, session: nil, related_request_id: nil)
430443
return ->(params) { handle_cancelled_notification(params, session: session) }
431444
end
432445

446+
case method
447+
when Methods::NOTIFICATIONS_ROOTS_LIST_CHANGED
448+
warn_if_deprecated_protocol_feature(:roots, session: session, uplevel: 2)
449+
when Methods::LOGGING_SET_LEVEL
450+
warn_if_deprecated_protocol_feature(:logging, session: session, uplevel: 2)
451+
end
452+
433453
handler = @handlers[method]
434454
unless handler
435455
instrument_call("unsupported_method", server_context: { request: request }) do
@@ -568,7 +588,11 @@ def init(params, session: nil)
568588
response_instructions = nil
569589
end
570590

571-
session&.mark_initialized!
591+
if session
592+
session.mark_initialized!(protocol_version: negotiated_version)
593+
else
594+
@client_protocol_version = negotiated_version
595+
end
572596

573597
{
574598
protocolVersion: negotiated_version,
@@ -594,6 +618,19 @@ def configure_logging_level(request, session: nil)
594618
{}
595619
end
596620

621+
def warn_if_deprecated_protocol_feature(feature, session: nil, uplevel: 1)
622+
protocol_version = effective_deprecation_protocol_version(session)
623+
MCP::ProtocolDeprecations.warn_for(feature, protocol_version: protocol_version, uplevel: uplevel)
624+
end
625+
626+
def effective_deprecation_protocol_version(session)
627+
session&.protocol_version || @client_protocol_version || explicit_protocol_version
628+
end
629+
630+
def explicit_protocol_version
631+
configuration.protocol_version if configuration.protocol_version?
632+
end
633+
597634
def list_tools(request)
598635
page = paginate(@tools.values, cursor: cursor_from(request), page_size: @page_size, request: request, &:to_h)
599636

lib/mcp/server_context.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ 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

@@ -51,6 +54,10 @@ def notify_resources_updated(uri:)
5154
end
5255

5356
# Delegates to the session so the request is scoped to the originating client.
57+
# @deprecated MCP Roots (`roots/list` and
58+
# `notifications/roots/list_changed`) is deprecated as of MCP protocol
59+
# version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs,
60+
# server configuration, or environment variables instead.
5461
def list_roots
5562
if @notification_target.respond_to?(:list_roots)
5663
@notification_target.list_roots(related_request_id: @related_request_id)
@@ -84,6 +91,9 @@ def ping
8491
# Delegates to the session so the request is scoped to the originating client.
8592
# Falls back to `@context` (via `method_missing`) when `@notification_target`
8693
# does not support sampling.
94+
# @deprecated MCP Sampling (`sampling/createMessage`) is deprecated as of
95+
# MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider
96+
# APIs instead.
8797
def create_sampling_message(**kwargs)
8898
if @notification_target.respond_to?(:create_sampling_message)
8999
@notification_target.create_sampling_message(**kwargs, related_request_id: @related_request_id)

lib/mcp/server_session.rb

Lines changed: 20 additions & 2 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,7 +100,13 @@ def client_capabilities
98100
end
99101

100102
# Sends a `roots/list` request scoped to this session.
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.
101107
def list_roots(related_request_id: nil)
108+
@server.send(:warn_if_deprecated_protocol_feature, :roots, session: self, uplevel: 2)
109+
102110
unless client_capabilities&.dig(:roots)
103111
raise "Client does not support roots."
104112
end
@@ -115,7 +123,12 @@ def ping(related_request_id: nil)
115123
end
116124

117125
# Sends a `sampling/createMessage` request scoped to this session.
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.
118129
def create_sampling_message(related_request_id: nil, **kwargs)
130+
@server.send(:warn_if_deprecated_protocol_feature, :sampling, session: self, uplevel: 2)
131+
119132
params = @server.build_sampling_params(client_capabilities, **kwargs)
120133
send_to_transport_request(Methods::SAMPLING_CREATE_MESSAGE, params, related_request_id: related_request_id)
121134
end
@@ -188,7 +201,12 @@ def notify_progress(progress_token:, progress:, total: nil, message: nil, relate
188201
end
189202

190203
# Sends a log message notification to this session only.
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.
191207
def notify_log_message(data:, level:, logger: nil, related_request_id: nil)
208+
@server.send(:warn_if_deprecated_protocol_feature, :logging, session: self, uplevel: 2)
209+
192210
effective_logging = @logging_message_notification || @server.logging_message_notification
193211
return unless effective_logging&.should_notify?(level)
194212

test/mcp/client/http_test.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
module MCP
1212
class Client
1313
class HTTPTest < Minitest::Test
14+
include DeprecationWarningTestHelper
15+
1416
def test_raises_load_error_when_faraday_not_available
1517
client = HTTP.new(url: url)
1618

@@ -801,6 +803,44 @@ def test_connect_accepts_custom_parameters
801803
assert_requested(notification_stub)
802804
end
803805

806+
def test_connect_warns_for_deprecated_capabilities_when_negotiated_protocol_version_is_2026_07_28
807+
notification_stub = stub_notification
808+
809+
init_stub = stub_request(:post, url)
810+
.with { |req| JSON.parse(req.body)["method"] == "initialize" }
811+
.to_return(
812+
status: 200,
813+
headers: { "Content-Type" => "application/json" },
814+
body: { result: { protocolVersion: "2026-07-28" } }.to_json,
815+
)
816+
817+
assert_deprecation_warning(/MCP Roots .*2026-07-28.*MCP Sampling .*2026-07-28/m) do
818+
client.connect(capabilities: { roots: { listChanged: true }, sampling: {} })
819+
end
820+
821+
assert_requested(init_stub)
822+
assert_requested(notification_stub)
823+
end
824+
825+
def test_connect_does_not_warn_for_deprecated_capabilities_when_negotiated_protocol_version_is_older
826+
notification_stub = stub_notification
827+
828+
init_stub = stub_request(:post, url)
829+
.with { |req| JSON.parse(req.body)["method"] == "initialize" }
830+
.to_return(
831+
status: 200,
832+
headers: { "Content-Type" => "application/json" },
833+
body: { result: { protocolVersion: "2025-11-25" } }.to_json,
834+
)
835+
836+
assert_no_deprecation_warning do
837+
client.connect(capabilities: { roots: { listChanged: true }, sampling: {} })
838+
end
839+
840+
assert_requested(init_stub)
841+
assert_requested(notification_stub)
842+
end
843+
804844
def test_connect_is_idempotent
805845
init_stub = stub_initialize
806846
notification_stub = stub_notification

0 commit comments

Comments
 (0)