From a2575b24e5f48943f4f0b66b6632b917fe803116 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 27 Mar 2026 11:47:34 +0900 Subject: [PATCH] Use accessor method in `server_context_with_meta` instead of ivar ## Motivation and Context `server_context_with_meta` reads `@server_context` (the instance variable) directly instead of calling the `server_context` accessor method. This prevents subclasses from overriding `server_context`. Ffor example, to provide thread-local context in multi-threaded servers like Puma. Fixes #271. ## How Has This Been Tested? Added a regression test and passed. ## Breaking Changes None. This restores the expected behavior of `attr_accessor :server_context`. --- lib/mcp/server.rb | 8 ++++---- test/mcp/server_test.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/mcp/server.rb b/lib/mcp/server.rb index 52552072..7646b296 100644 --- a/lib/mcp/server.rb +++ b/lib/mcp/server.rb @@ -531,14 +531,14 @@ def call_prompt_template_with_args(prompt, args, server_context) def server_context_with_meta(request) meta = request[:_meta] - if meta && @server_context.is_a?(Hash) - context = @server_context.dup + if meta && server_context.is_a?(Hash) + context = server_context.dup context[:_meta] = meta context - elsif meta && @server_context.nil? + elsif meta && server_context.nil? { _meta: meta } else - @server_context + server_context end end end diff --git a/test/mcp/server_test.rb b/test/mcp/server_test.rb index fac919b6..c7d7ec7d 100644 --- a/test/mcp/server_test.rb +++ b/test/mcp/server_test.rb @@ -1731,5 +1731,31 @@ def call(numbers:, strings:, objects:, server_context: nil) end end end + + test "server_context_with_meta uses accessor method, not ivar directly" do + subclass = Class.new(Server) do + def server_context + { custom: "from_accessor" } + end + end + + server = subclass.new(name: "test", tools: []) + + received_context = nil + server.define_tool(name: "ctx_tool") do |server_context:| + received_context = server_context + Tool::Response.new([{ type: "text", text: "ok" }]) + end + + request = { + jsonrpc: "2.0", + method: "tools/call", + id: 1, + params: { name: "ctx_tool", arguments: {} }, + } + + server.handle(request) + assert_equal "from_accessor", received_context[:custom] + end end end