Skip to content

server_context_with_meta reads @server_context ivar instead of accessor method #271

@AndyGauge

Description

@AndyGauge

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
end

This 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
end

Without 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
end

This is standard Ruby convention — when an attr_accessor is defined, internal code should use the method so subclasses can override behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions