-
Notifications
You must be signed in to change notification settings - Fork 103
Description
Problem
server_context_with_meta in lib/mcp/server.rb reads @server_context (the instance variable) directly instead of calling the server_context accessor method:
def server_context_with_meta(request)
meta = request[:_meta]
if meta && @server_context.is_a?(Hash)
context = @server_context.dup
context[:_meta] = meta
context
elsif meta && @server_context.nil?
{ _meta: meta }
else
@server_context
end
endThis means subclasses that override server_context have their override bypassed. The attr_accessor :server_context on line 50 suggests the intent is to use the method, but the implementation reads the ivar.
Use case
We run an MCP server under Puma (multi-threaded). We need server_context to be thread-local so concurrent requests don't clobber each other's user context:
class HireMcpServer < MCP::Server
def server_context
Thread.current[:mcp_server_context]
end
def server_context=(ctx)
Thread.current[:mcp_server_context] = ctx
@server_context = ctx # workaround: also write ivar for server_context_with_meta
end
endWithout the @server_context = ctx workaround, tools receive nil for server_context because server_context_with_meta reads the ivar (which is never set) instead of calling our overridden method.
Suggested fix
Replace @server_context with server_context in server_context_with_meta:
def server_context_with_meta(request)
meta = request[:_meta]
if meta && server_context.is_a?(Hash)
context = server_context.dup
context[:_meta] = meta
context
elsif meta && server_context.nil?
{ _meta: meta }
else
server_context
end
endThis is standard Ruby convention — when an attr_accessor is defined, internal code should use the method so subclasses can override behavior.