diff --git a/CHANGELOG.md b/CHANGELOG.md index c020db62..35932d0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Warn when deprecated Roots, Sampling, or Logging APIs are used with protocol version `2026-07-28` (#390) + ## [0.20.0] - 2026-06-14 ### Added diff --git a/lib/mcp.rb b/lib/mcp.rb index 867eb02e..68df30a3 100644 --- a/lib/mcp.rb +++ b/lib/mcp.rb @@ -2,6 +2,7 @@ require_relative "json_rpc_handler" require_relative "mcp/configuration" +require_relative "mcp/protocol_deprecations" require_relative "mcp/string_utils" require_relative "mcp/transport" require_relative "mcp/version" diff --git a/lib/mcp/client/http.rb b/lib/mcp/client/http.rb index 273a756a..e4093454 100644 --- a/lib/mcp/client/http.rb +++ b/lib/mcp/client/http.rb @@ -4,6 +4,7 @@ require_relative "../../json_rpc_handler" require_relative "../configuration" require_relative "../methods" +require_relative "../protocol_deprecations" require_relative "../version" module MCP @@ -156,6 +157,8 @@ def connect(client_info: nil, protocol_version: nil, capabilities: {}) ) end + MCP::ProtocolDeprecations.warn_for_client_capabilities(capabilities, protocol_version: negotiated_protocol_version, uplevel: 1) + begin send_request(request: { jsonrpc: JsonRpcHandler::Version::V2_0, diff --git a/lib/mcp/client/stdio.rb b/lib/mcp/client/stdio.rb index a273c579..ccb39af9 100644 --- a/lib/mcp/client/stdio.rb +++ b/lib/mcp/client/stdio.rb @@ -7,6 +7,7 @@ require_relative "../../json_rpc_handler" require_relative "../configuration" require_relative "../methods" +require_relative "../protocol_deprecations" require_relative "../version" module MCP @@ -110,6 +111,8 @@ def connect(client_info: nil, protocol_version: nil, capabilities: {}) ) end + MCP::ProtocolDeprecations.warn_for_client_capabilities(capabilities, protocol_version: negotiated_protocol_version, uplevel: 1) + begin notification = { jsonrpc: JsonRpcHandler::Version::V2_0, diff --git a/lib/mcp/configuration.rb b/lib/mcp/configuration.rb index 70acb50d..1f816082 100644 --- a/lib/mcp/configuration.rb +++ b/lib/mcp/configuration.rb @@ -3,8 +3,13 @@ module MCP class Configuration LATEST_STABLE_PROTOCOL_VERSION = "2025-11-25" + ROOTS_SAMPLING_LOGGING_DEPRECATED_PROTOCOL_VERSION = "2026-07-28" SUPPORTED_STABLE_PROTOCOL_VERSIONS = [ - LATEST_STABLE_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", + ROOTS_SAMPLING_LOGGING_DEPRECATED_PROTOCOL_VERSION, + LATEST_STABLE_PROTOCOL_VERSION, + "2025-06-18", + "2025-03-26", + "2024-11-05", ] DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26" diff --git a/lib/mcp/protocol_deprecations.rb b/lib/mcp/protocol_deprecations.rb new file mode 100644 index 00000000..834ebd21 --- /dev/null +++ b/lib/mcp/protocol_deprecations.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require_relative "configuration" + +module MCP + module ProtocolDeprecations + extend self + + ROOTS_MESSAGE = + "MCP Roots (`roots/list` and `notifications/roots/list_changed`) is deprecated as of protocol version " \ + "2026-07-28 (SEP-2577). Use tool parameters, resource URIs, server configuration, or environment " \ + "variables instead." + SAMPLING_MESSAGE = + "MCP Sampling (`sampling/createMessage`) is deprecated as of protocol version 2026-07-28 (SEP-2577). " \ + "Use direct LLM provider APIs instead." + LOGGING_MESSAGE = + "MCP Logging (`logging/setLevel` and `notifications/message`) is deprecated as of protocol version " \ + "2026-07-28 (SEP-2577). Use stderr or OpenTelemetry instead." + + MESSAGES = { + roots: ROOTS_MESSAGE, + sampling: SAMPLING_MESSAGE, + logging: LOGGING_MESSAGE, + }.freeze + + def deprecated_roots_sampling_logging?(protocol_version) + protocol_version == Configuration::ROOTS_SAMPLING_LOGGING_DEPRECATED_PROTOCOL_VERSION + end + + def warn_for(feature, protocol_version:, uplevel: 1) + return unless deprecated_roots_sampling_logging?(protocol_version) + + Kernel.warn(MESSAGES.fetch(feature), uplevel: uplevel) + end + + def warn_for_client_capabilities(capabilities, protocol_version:, uplevel: 1) + return unless deprecated_roots_sampling_logging?(protocol_version) + return unless capabilities + + warn_for(:roots, protocol_version: protocol_version, uplevel: uplevel) if capability?(capabilities, :roots) + warn_for(:sampling, protocol_version: protocol_version, uplevel: uplevel) if capability?(capabilities, :sampling) + end + + private + + def capability?(capabilities, key) + capabilities.key?(key) || capabilities.key?(key.to_s) + end + end +end diff --git a/lib/mcp/server.rb b/lib/mcp/server.rb index e734b7bb..ee1d00c2 100644 --- a/lib/mcp/server.rb +++ b/lib/mcp/server.rb @@ -7,6 +7,7 @@ require_relative "methods" require_relative "logging_message_notification" require_relative "progress" +require_relative "protocol_deprecations" require_relative "server_context" require_relative "server/capabilities" require_relative "server/pagination" @@ -140,6 +141,7 @@ def initialize( self.page_size = page_size @configuration = MCP.configuration.merge(configuration) @client = nil + @client_protocol_version = nil validate! @@ -256,7 +258,12 @@ def notify_resources_list_changed report_exception(e, { notification: "resources_list_changed" }) end + # @deprecated MCP Logging (`logging/setLevel` and `notifications/message`) + # is deprecated as of MCP protocol version 2026-07-28 (SEP-2577). + # Use stderr or OpenTelemetry instead. def notify_log_message(data:, level:, logger: nil) + warn_if_deprecated_protocol_feature(:logging, uplevel: 1) + return unless @transport return unless logging_message_notification&.should_notify?(level) @@ -272,7 +279,13 @@ def notify_log_message(data:, level:, logger: nil) # Called when a client notifies the server that its filesystem roots have changed. # # @yield [params] The notification params (typically `nil`). + # @deprecated MCP Roots (`roots/list` and + # `notifications/roots/list_changed`) is deprecated as of MCP protocol + # version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs, + # server configuration, or environment variables instead. def roots_list_changed_handler(&block) + warn_if_deprecated_protocol_feature(:roots, uplevel: 1) + @handlers[Methods::NOTIFICATIONS_ROOTS_LIST_CHANGED] = block end @@ -430,6 +443,13 @@ def handle_request(request, method, session: nil, related_request_id: nil) return ->(params) { handle_cancelled_notification(params, session: session) } end + case method + when Methods::NOTIFICATIONS_ROOTS_LIST_CHANGED + warn_if_deprecated_protocol_feature(:roots, session: session, uplevel: 2) + when Methods::LOGGING_SET_LEVEL + warn_if_deprecated_protocol_feature(:logging, session: session, uplevel: 2) + end + handler = @handlers[method] unless handler instrument_call("unsupported_method", server_context: { request: request }) do @@ -568,7 +588,11 @@ def init(params, session: nil) response_instructions = nil end - session&.mark_initialized! + if session + session.mark_initialized!(protocol_version: negotiated_version) + else + @client_protocol_version = negotiated_version + end { protocolVersion: negotiated_version, @@ -594,6 +618,19 @@ def configure_logging_level(request, session: nil) {} end + def warn_if_deprecated_protocol_feature(feature, session: nil, uplevel: 1) + protocol_version = effective_deprecation_protocol_version(session) + MCP::ProtocolDeprecations.warn_for(feature, protocol_version: protocol_version, uplevel: uplevel) + end + + def effective_deprecation_protocol_version(session) + session&.protocol_version || @client_protocol_version || explicit_protocol_version + end + + def explicit_protocol_version + configuration.protocol_version if configuration.protocol_version? + end + def list_tools(request) page = paginate(@tools.values, cursor: cursor_from(request), page_size: @page_size, request: request, &:to_h) diff --git a/lib/mcp/server_context.rb b/lib/mcp/server_context.rb index baed180b..f758b078 100644 --- a/lib/mcp/server_context.rb +++ b/lib/mcp/server_context.rb @@ -35,6 +35,9 @@ def report_progress(progress, total: nil, message: nil) # @param data [Object] The log data to send. # @param level [String] Log level (e.g., `"debug"`, `"info"`, `"error"`). # @param logger [String, nil] Logger name. + # @deprecated MCP Logging (`logging/setLevel` and `notifications/message`) + # is deprecated as of MCP protocol version 2026-07-28 (SEP-2577). + # Use stderr or OpenTelemetry instead. def notify_log_message(data:, level:, logger: nil) return unless @notification_target @@ -51,6 +54,10 @@ def notify_resources_updated(uri:) end # Delegates to the session so the request is scoped to the originating client. + # @deprecated MCP Roots (`roots/list` and + # `notifications/roots/list_changed`) is deprecated as of MCP protocol + # version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs, + # server configuration, or environment variables instead. def list_roots if @notification_target.respond_to?(:list_roots) @notification_target.list_roots(related_request_id: @related_request_id) @@ -84,6 +91,9 @@ def ping # Delegates to the session so the request is scoped to the originating client. # Falls back to `@context` (via `method_missing`) when `@notification_target` # does not support sampling. + # @deprecated MCP Sampling (`sampling/createMessage`) is deprecated as of + # MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider + # APIs instead. def create_sampling_message(**kwargs) if @notification_target.respond_to?(:create_sampling_message) @notification_target.create_sampling_message(**kwargs, related_request_id: @related_request_id) diff --git a/lib/mcp/server_session.rb b/lib/mcp/server_session.rb index 307217fb..fb6ba83f 100644 --- a/lib/mcp/server_session.rb +++ b/lib/mcp/server_session.rb @@ -7,7 +7,7 @@ module MCP # Holds per-connection state for a single client session. # Created by the transport layer; delegates request handling to the shared `Server`. class ServerSession - attr_reader :session_id, :client, :logging_message_notification + attr_reader :session_id, :client, :logging_message_notification, :protocol_version def initialize(server:, transport:, session_id: nil) @server = server @@ -16,6 +16,7 @@ def initialize(server:, transport:, session_id: nil) @client = nil @client_capabilities = nil @logging_message_notification = nil + @protocol_version = nil @in_flight = {} @in_flight_mutex = Mutex.new @initialized = false @@ -29,8 +30,9 @@ def initialized? # Called by `Server#init` after a successful `initialize` response, so subsequent # `initialize` requests on the same session can be rejected per MCP spec # (the initialization phase MUST be the first interaction). - def mark_initialized! + def mark_initialized!(protocol_version: nil) @initialized = true + @protocol_version = protocol_version end # Registers a `Cancellation` token for an in-flight request. @@ -98,7 +100,13 @@ def client_capabilities end # Sends a `roots/list` request scoped to this session. + # @deprecated MCP Roots (`roots/list` and + # `notifications/roots/list_changed`) is deprecated as of MCP protocol + # version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs, + # server configuration, or environment variables instead. def list_roots(related_request_id: nil) + @server.send(:warn_if_deprecated_protocol_feature, :roots, session: self, uplevel: 2) + unless client_capabilities&.dig(:roots) raise "Client does not support roots." end @@ -115,7 +123,12 @@ def ping(related_request_id: nil) end # Sends a `sampling/createMessage` request scoped to this session. + # @deprecated MCP Sampling (`sampling/createMessage`) is deprecated as of + # MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider + # APIs instead. def create_sampling_message(related_request_id: nil, **kwargs) + @server.send(:warn_if_deprecated_protocol_feature, :sampling, session: self, uplevel: 2) + params = @server.build_sampling_params(client_capabilities, **kwargs) send_to_transport_request(Methods::SAMPLING_CREATE_MESSAGE, params, related_request_id: related_request_id) end @@ -188,7 +201,12 @@ def notify_progress(progress_token:, progress:, total: nil, message: nil, relate end # Sends a log message notification to this session only. + # @deprecated MCP Logging (`logging/setLevel` and `notifications/message`) + # is deprecated as of MCP protocol version 2026-07-28 (SEP-2577). + # Use stderr or OpenTelemetry instead. def notify_log_message(data:, level:, logger: nil, related_request_id: nil) + @server.send(:warn_if_deprecated_protocol_feature, :logging, session: self, uplevel: 2) + effective_logging = @logging_message_notification || @server.logging_message_notification return unless effective_logging&.should_notify?(level) diff --git a/test/mcp/client/http_test.rb b/test/mcp/client/http_test.rb index 5a90fde8..395f5418 100644 --- a/test/mcp/client/http_test.rb +++ b/test/mcp/client/http_test.rb @@ -11,6 +11,8 @@ module MCP class Client class HTTPTest < Minitest::Test + include DeprecationWarningTestHelper + def test_raises_load_error_when_faraday_not_available client = HTTP.new(url: url) @@ -801,6 +803,44 @@ def test_connect_accepts_custom_parameters assert_requested(notification_stub) end + def test_connect_warns_for_deprecated_capabilities_when_negotiated_protocol_version_is_2026_07_28 + notification_stub = stub_notification + + init_stub = stub_request(:post, url) + .with { |req| JSON.parse(req.body)["method"] == "initialize" } + .to_return( + status: 200, + headers: { "Content-Type" => "application/json" }, + body: { result: { protocolVersion: "2026-07-28" } }.to_json, + ) + + assert_deprecation_warning(/MCP Roots .*2026-07-28.*MCP Sampling .*2026-07-28/m) do + client.connect(capabilities: { roots: { listChanged: true }, sampling: {} }) + end + + assert_requested(init_stub) + assert_requested(notification_stub) + end + + def test_connect_does_not_warn_for_deprecated_capabilities_when_negotiated_protocol_version_is_older + notification_stub = stub_notification + + init_stub = stub_request(:post, url) + .with { |req| JSON.parse(req.body)["method"] == "initialize" } + .to_return( + status: 200, + headers: { "Content-Type" => "application/json" }, + body: { result: { protocolVersion: "2025-11-25" } }.to_json, + ) + + assert_no_deprecation_warning do + client.connect(capabilities: { roots: { listChanged: true }, sampling: {} }) + end + + assert_requested(init_stub) + assert_requested(notification_stub) + end + def test_connect_is_idempotent init_stub = stub_initialize notification_stub = stub_notification diff --git a/test/mcp/client/stdio_test.rb b/test/mcp/client/stdio_test.rb index 6b1b92fb..779d9415 100644 --- a/test/mcp/client/stdio_test.rb +++ b/test/mcp/client/stdio_test.rb @@ -9,6 +9,8 @@ module MCP class Client class StdioTest < Minitest::Test + include DeprecationWarningTestHelper + IMPLICIT_CONNECT_DEPRECATION_WARNING = /Calling `MCP::Client::Stdio#send_request` without calling `MCP::Client#connect` is deprecated\. Use `MCP::Client#connect` before sending requests instead\./.freeze @@ -919,6 +921,38 @@ def test_connect_accepts_custom_parameters stdout_write.close end + def test_connect_warns_for_deprecated_capabilities_when_negotiated_protocol_version_is_2026_07_28 + stdin_read, stdin_write = IO.pipe + stdout_read, stdout_write = IO.pipe + stderr_read, _ = IO.pipe + + Open3.stubs(:popen3).returns([stdin_write, stdout_read, stderr_read, mock_wait_thread]) + + transport = Stdio.new(command: "ruby", args: ["server.rb"]) + + server_thread = Thread.new do + init_line = stdin_read.gets + init_request = JSON.parse(init_line) + stdout_write.puts(JSON.generate( + jsonrpc: "2.0", + id: init_request["id"], + result: { protocolVersion: "2026-07-28" }, + )) + stdout_write.flush + stdin_read.gets + end + + assert_deprecation_warning(/MCP Roots .*2026-07-28.*MCP Sampling .*2026-07-28/m) do + transport.connect(capabilities: { roots: { listChanged: true }, sampling: {} }) + end + ensure + server_thread.join + stdin_read.close + stdin_write.close + stdout_read.close + stdout_write.close + end + def test_connect_raises_on_jsonrpc_error_response stdin_read, stdin_write = IO.pipe stdout_read, stdout_write = IO.pipe diff --git a/test/mcp/configuration_test.rb b/test/mcp/configuration_test.rb index 8270b101..92eaf053 100644 --- a/test/mcp/configuration_test.rb +++ b/test/mcp/configuration_test.rb @@ -54,7 +54,7 @@ class ConfigurationTest < ActiveSupport::TestCase Configuration.new(protocol_version: "DRAFT-2025-v3") end - assert_equal("protocol_version must be 2025-11-25, 2025-06-18, 2025-03-26, or 2024-11-05", exception.message) + assert_equal("protocol_version must be 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26, or 2024-11-05", exception.message) end test "raises ArgumentError when protocol_version is not a supported protocol version" do @@ -63,7 +63,7 @@ class ConfigurationTest < ActiveSupport::TestCase custom_version = "2025-03-27" config.protocol_version = custom_version end - assert_equal("protocol_version must be 2025-11-25, 2025-06-18, 2025-03-26, or 2024-11-05", exception.message) + assert_equal("protocol_version must be 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26, or 2024-11-05", exception.message) end test "raises ArgumentError when protocol_version is not a boolean value" do diff --git a/test/mcp/server_notification_test.rb b/test/mcp/server_notification_test.rb index 74a89cc9..24247e43 100644 --- a/test/mcp/server_notification_test.rb +++ b/test/mcp/server_notification_test.rb @@ -5,6 +5,7 @@ module MCP class ServerNotificationTest < ActiveSupport::TestCase include InstrumentationTestHelper + include DeprecationWarningTestHelper class MockTransport < Transport attr_reader :notifications @@ -74,6 +75,90 @@ def handle_request(request); end assert_equal({ "data" => { error: "Connection Failed" }, "level" => "error" }, @mock_transport.notifications.first[:params]) end + test "#notify_log_message warns when configured protocol version is 2026-07-28" do + server = Server.new( + name: "test_server", + version: "1.0.0", + configuration: Configuration.new(protocol_version: "2026-07-28"), + ) + mock_transport = MockTransport.new(server) + server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "error") + + assert_deprecation_warning(/MCP Logging .*2026-07-28/) do + server.notify_log_message(data: { error: "Connection Failed" }, level: "error") + end + + assert_equal Methods::NOTIFICATIONS_MESSAGE, mock_transport.notifications.first[:method] + end + + test "#notify_log_message warns when configured protocol version is 2026-07-28 without transport" do + server = Server.new( + name: "test_server", + version: "1.0.0", + configuration: Configuration.new(protocol_version: "2026-07-28"), + ) + server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "error") + + assert_deprecation_warning(/MCP Logging .*2026-07-28/) do + server.notify_log_message(data: { error: "Connection Failed" }, level: "error") + end + end + + test "#notify_log_message warns when configured protocol version is 2026-07-28 below configured level" do + server = Server.new( + name: "test_server", + version: "1.0.0", + configuration: Configuration.new(protocol_version: "2026-07-28"), + ) + mock_transport = MockTransport.new(server) + server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "error") + + assert_deprecation_warning(/MCP Logging .*2026-07-28/) do + server.notify_log_message(data: { message: "test" }, level: "info") + end + + assert_empty mock_transport.notifications + end + + test "#notify_log_message does not warn when configured protocol version is older" do + server = Server.new( + name: "test_server", + version: "1.0.0", + configuration: Configuration.new(protocol_version: "2025-11-25"), + ) + MockTransport.new(server) + server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "error") + + assert_no_deprecation_warning do + server.notify_log_message(data: { error: "Connection Failed" }, level: "error") + end + end + + test "ServerSession#notify_log_message warns when negotiated protocol version is 2026-07-28 below configured level" do + server = Server.new(name: "test_server", version: "1.0.0") + mock_transport = MockTransport.new(server) + session = ServerSession.new(server: server, transport: mock_transport) + server.handle( + { + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + protocolVersion: "2026-07-28", + clientInfo: { name: "test-client", version: "1.0" }, + }, + }, + session: session, + ) + session.configure_logging(MCP::LoggingMessageNotification.new(level: "error")) + + assert_deprecation_warning(/MCP Logging .*2026-07-28/) do + session.notify_log_message(data: { message: "test" }, level: "info") + end + + assert_empty mock_transport.notifications + end + test "#notify_log_message sends notification with logger through transport" do @server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "error") @server.notify_log_message(data: { error: "Connection Failed" }, level: "error", logger: "DatabaseLogger") diff --git a/test/mcp/server_roots_test.rb b/test/mcp/server_roots_test.rb index c887398f..e4af614c 100644 --- a/test/mcp/server_roots_test.rb +++ b/test/mcp/server_roots_test.rb @@ -4,6 +4,8 @@ module MCP class ServerRootsTest < ActiveSupport::TestCase + include DeprecationWarningTestHelper + class MockTransport < Transport attr_reader :requests @@ -62,6 +64,36 @@ def close; end assert callback_called end + test "notifications/roots/list_changed warns when negotiated protocol version is 2026-07-28" do + server = Server.new(name: "test", version: "1.0") + server.handle({ + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + protocolVersion: "2026-07-28", + capabilities: { roots: { listChanged: true } }, + clientInfo: { name: "test-client", version: "1.0" }, + }, + }) + + assert_deprecation_warning(/MCP Roots .*2026-07-28/) do + server.handle({ + jsonrpc: "2.0", + method: "notifications/roots/list_changed", + }) + end + end + + test "notifications/roots/list_changed does not warn when negotiated protocol version is older" do + assert_no_deprecation_warning do + @server.handle({ + jsonrpc: "2.0", + method: "notifications/roots/list_changed", + }) + end + end + test "notifications/roots/list_changed is handled as no-op by default" do result = @server.handle({ jsonrpc: "2.0", @@ -123,6 +155,21 @@ def close; end assert_equal("Client does not support roots.", error.message) end + test "ServerSession#list_roots warns when negotiated protocol version is 2026-07-28 and client lacks roots" do + session = ServerSession.new(server: @server, transport: @mock_transport) + session.store_client_info(client: { name: "test-client" }, capabilities: {}) + session.mark_initialized!(protocol_version: "2026-07-28") + + error = nil + assert_deprecation_warning(/MCP Roots .*2026-07-28/) do + error = assert_raises(RuntimeError) do + session.list_roots + end + end + + assert_equal("Client does not support roots.", error.message) + end + test "ServerSession#client_capabilities falls back to server global capabilities" do transport = MCP::Server::Transports::StreamableHTTPTransport.new(@server) @@ -198,5 +245,31 @@ def close; end end assert_equal("No active stream for roots/list request.", error.message) end + + test "ServerSession#list_roots warns when session negotiated protocol version is 2026-07-28" do + server = Server.new(name: "test", version: "1.0") + transport = MockTransport.new(server) + + session = ServerSession.new(server: server, transport: transport, session_id: "s1") + server.handle( + { + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + protocolVersion: "2026-07-28", + capabilities: { roots: {} }, + clientInfo: { name: "http-client", version: "1.0" }, + }, + }, + session: session, + ) + + assert_deprecation_warning(/MCP Roots .*2026-07-28/) do + session.list_roots + end + + assert_equal Methods::ROOTS_LIST, transport.requests.first[:method] + end end end diff --git a/test/mcp/server_sampling_test.rb b/test/mcp/server_sampling_test.rb index dd123564..78d473f4 100644 --- a/test/mcp/server_sampling_test.rb +++ b/test/mcp/server_sampling_test.rb @@ -5,6 +5,7 @@ module MCP class ServerSamplingTest < ActiveSupport::TestCase include InstrumentationTestHelper + include DeprecationWarningTestHelper class MockTransport < Transport attr_reader :requests @@ -62,6 +63,28 @@ def close; end assert_equal "Response from LLM", result[:content][:text] end + test "create_sampling_message warns when session negotiated protocol version is 2026-07-28" do + @session.mark_initialized!(protocol_version: "2026-07-28") + + assert_deprecation_warning(/MCP Sampling .*2026-07-28/) do + @session.create_sampling_message( + messages: [{ role: "user", content: { type: "text", text: "Hello" } }], + max_tokens: 100, + ) + end + end + + test "create_sampling_message does not warn when session negotiated protocol version is older" do + @session.mark_initialized!(protocol_version: "2025-11-25") + + assert_no_deprecation_warning do + @session.create_sampling_message( + messages: [{ role: "user", content: { type: "text", text: "Hello" } }], + max_tokens: 100, + ) + end + end + test "create_sampling_message sends all optional params" do @session.create_sampling_message( messages: [{ role: "user", content: { type: "text", text: "Hello" } }], @@ -98,6 +121,23 @@ def close; end assert_equal("Client does not support sampling.", error.message) end + test "create_sampling_message warns when negotiated protocol version is 2026-07-28 and client lacks sampling" do + @session.store_client_info(client: { name: "test-client" }, capabilities: {}) + @session.mark_initialized!(protocol_version: "2026-07-28") + + error = nil + assert_deprecation_warning(/MCP Sampling .*2026-07-28/) do + error = assert_raises(RuntimeError) do + @session.create_sampling_message( + messages: [{ role: "user", content: { type: "text", text: "Hello" } }], + max_tokens: 100, + ) + end + end + + assert_equal("Client does not support sampling.", error.message) + end + test "create_sampling_message raises error when tools used but client lacks sampling.tools" do error = assert_raises(RuntimeError) do @session.create_sampling_message( diff --git a/test/mcp/server_test.rb b/test/mcp/server_test.rb index 43ece74e..fc095961 100644 --- a/test/mcp/server_test.rb +++ b/test/mcp/server_test.rb @@ -5,6 +5,7 @@ module MCP class ServerTest < ActiveSupport::TestCase include InstrumentationTestHelper + include DeprecationWarningTestHelper setup do @tool = Tool.define( name: "test_tool", @@ -847,6 +848,65 @@ class Example < Tool refute response.key?(:error) end + test "#configure_logging_level warns when negotiated protocol version is 2026-07-28" do + server = Server.new(tools: [TestTool]) + server.handle( + { + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + protocolVersion: "2026-07-28", + clientInfo: { name: "test-client", version: "1.0" }, + }, + }, + ) + + response = nil + assert_deprecation_warning(/MCP Logging .*2026-07-28/) do + response = server.handle( + { + jsonrpc: "2.0", + id: 2, + method: "logging/setLevel", + params: { + level: "info", + }, + }, + ) + end + + assert_empty response[:result] + end + + test "#configure_logging_level does not warn when negotiated protocol version is older" do + server = Server.new(tools: [TestTool]) + server.handle( + { + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + protocolVersion: "2025-11-25", + clientInfo: { name: "test-client", version: "1.0" }, + }, + }, + ) + + assert_no_deprecation_warning do + server.handle( + { + jsonrpc: "2.0", + id: 2, + method: "logging/setLevel", + params: { + level: "info", + }, + }, + ) + end + end + test "#configure_logging_level returns an error object when invalid log level is provided" do server = Server.new( tools: [TestTool], @@ -898,6 +958,35 @@ class Example < Tool assert_includes response[:error][:data], "Server does not support logging" end + test "#configure_logging_level warns when configured protocol version is 2026-07-28 and server lacks logging capability" do + server = Server.new( + tools: [TestTool], + configuration: Configuration.new(protocol_version: "2026-07-28"), + capabilities: { + tools: { listChanged: true }, + prompts: { listChanged: true }, + resources: { listChanged: true }, + }, + ) + + response = nil + assert_deprecation_warning(/MCP Logging .*2026-07-28/) do + response = server.handle( + { + jsonrpc: "2.0", + id: 1, + method: "logging/setLevel", + params: { + level: "debug", + }, + }, + ) + end + + assert_equal(-32603, response[:error][:code]) + assert_includes response[:error][:data], "Server does not support logging" + end + test "#handle method with missing required top-level capability returns an error" do @server.capabilities = {} diff --git a/test/test_helper.rb b/test/test_helper.rb index 19e9974b..7fa402f0 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,3 +12,21 @@ require "sorbet-runtime" if RUBY_VERSION >= "3.0" require_relative "instrumentation_test_helper" + +module DeprecationWarningTestHelper + def assert_deprecation_warning(message_pattern, &block) + original_verbose = $VERBOSE + $VERBOSE = false + assert_output(nil, message_pattern, &block) + ensure + $VERBOSE = original_verbose + end + + def assert_no_deprecation_warning(&block) + original_verbose = $VERBOSE + $VERBOSE = false + assert_output(nil, "", &block) + ensure + $VERBOSE = original_verbose + end +end