From c4c73e05f522695b14b44471a7869afd46850226 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 19 Jun 2026 21:17:52 +0900 Subject: [PATCH] Fix a `SyntaxError` on Ruby 2.7.0 caused by arguments forwarding syntax ## Motivation and Context The gemspec already declares `required_ruby_version >= 2.7.0`, but `ServerContext` used the `...` arguments forwarding syntax in `method_missing`. That syntax was added in Ruby 2.7.3, so requiring the gem raised a `SyntaxError` on Ruby 2.7.0 through 2.7.2, and the library could not be loaded at all. RuboCop did not catch this because its Parser backend runs on Ruby 2.7.8, where the syntax is valid. CI missed it too because the test matrix tested `2.7`, which resolves to the latest 2.7.x rather than the declared minimum. ## How Has This Been Tested? Forward arguments explicitly with `*args, **kwargs, &block` so the method loads on Ruby 2.7.0, and pin the CI test matrix to 2.7.0 so the suite runs against the declared minimum instead of the latest 2.7.x. Running the suite on 2.7.0 also requires a `Hash.ruby2_keywords_hash?` shim in the test helper, since mocha would otherwise raise a `NoMethodError` on that version. ## Breaking Changes None. Closes #418 --- .github/workflows/ci.yml | 2 +- lib/mcp/server_context.rb | 9 +++++++-- test/mcp/server_context_test.rb | 14 ++++++++++++++ test/test_helper.rb | 10 ++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9077935a..66107452 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: entry: - - { ruby: '2.7', allowed-failure: false } + - { ruby: '2.7.0', allowed-failure: false } # Minimum supported Ruby version. - { ruby: '3.0', allowed-failure: false } - { ruby: '3.1', allowed-failure: false } - { ruby: '3.2', allowed-failure: false } diff --git a/lib/mcp/server_context.rb b/lib/mcp/server_context.rb index baed180b..98739842 100644 --- a/lib/mcp/server_context.rb +++ b/lib/mcp/server_context.rb @@ -130,9 +130,14 @@ def notify_elicitation_complete(**kwargs) end end - def method_missing(name, ...) + # Forward arguments explicitly with `*args, **kwargs, &block` rather than the `...` forwarding syntax. + # The gem supports Ruby 2.7.0 (see `required_ruby_version`), but RuboCop's Parser backend only runs on Ruby 2.7.8, + # so leading-argument forwarding like `def method_missing(name, ...)` is allowed by the linter even though it + # raises a `SyntaxError` on Ruby 2.7.0 through 2.7.2 (it was added in Ruby 2.7.3). Explicit forwarding keeps + # this method loadable on Ruby 2.7.0. + def method_missing(name, *args, **kwargs, &block) if @context.respond_to?(name) - @context.public_send(name, ...) + @context.public_send(name, *args, **kwargs, &block) else super end diff --git a/test/mcp/server_context_test.rb b/test/mcp/server_context_test.rb index 86fd972d..44fa12e4 100644 --- a/test/mcp/server_context_test.rb +++ b/test/mcp/server_context_test.rb @@ -201,6 +201,20 @@ def context.custom_method assert_equal "custom_value", server_context.custom_method end + test "ServerContext forwards positional, keyword, and block arguments to the context" do + context = Object.new + def context.combine(prefix, suffix:, &block) + block.call("#{prefix}-#{suffix}") + end + progress = Progress.new(notification_target: mock, progress_token: nil) + + server_context = ServerContext.new(context, progress: progress, notification_target: mock) + + result = server_context.combine("a", suffix: "b", &:upcase) + + assert_equal "A-B", result + end + test "ServerContext#report_progress works with nil context" do progress = mock progress.expects(:report).with(50, total: 100, message: nil).once diff --git a/test/test_helper.rb b/test/test_helper.rb index 19e9974b..b89bffad 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -6,6 +6,16 @@ require "minitest/mock" require "mocha/minitest" +# mocha relies on `Hash.ruby2_keywords_hash?`, which is absent on Ruby 2.7.0 and the earlier 2.7.x releases +# before it was backported. Those versions also lack the flag-setting APIs, so no hash is ever flagged as +# ruby2_keywords and returning `false` is correct. Without this shim, mocha raises `NoMethodError` and +# the suite cannot run on Ruby 2.7.0. +unless Hash.respond_to?(:ruby2_keywords_hash?) + def Hash.ruby2_keywords_hash?(_hash) + false + end +end + require "active_support" require "active_support/test_case"