Class: RubyLLM::MCP::Adapters::MCPSdkAdapter

Inherits:
BaseAdapter
  • Object
show all
Defined in:
lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb

Instance Attribute Summary collapse

Attributes inherited from BaseAdapter

#client

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseAdapter

#completion_prompt, #completion_resource, #elicitation_response, #error_response, #initialize_notification, #ping_response, #register_resource, #resources_subscribe, #resources_unsubscribe, #roots_list_change_notification, #roots_list_response, #sampling_create_message_response, support?, supported_features, supported_transports, supports, #supports?, supports_transport, #task_cancel, #task_get, #task_result, #task_status_notification, #tasks_list, transport_supported?, #validate_transport!

Constructor Details

#initialize(client, transport_type:, config: {}) ⇒ MCPSdkAdapter

Returns a new instance of MCPSdkAdapter.



23
24
25
26
27
28
29
30
31
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 23

def initialize(client, transport_type:, config: {})
  validate_transport!(transport_type)
  require_mcp_gem!
  super

  @mcp_client = nil
  @notification_handler = NotificationHandler.new(client)
  warn_passive_extension_support! if configured_extensions?
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



21
22
23
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 21

def config
  @config
end

#mcp_clientObject (readonly)

Returns the value of attribute mcp_client.



21
22
23
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 21

def mcp_client
  @mcp_client
end

#transport_typeObject (readonly)

Returns the value of attribute transport_type.



21
22
23
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 21

def transport_type
  @transport_type
end

Class Method Details

.warn_passive_extension_support_onceObject



397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 397

def warn_passive_extension_support_once
  @extensions_warning_mutex ||= Mutex.new

  @extensions_warning_mutex.synchronize do
    return if @extensions_warning_emitted

    RubyLLM::MCP.logger.warn(
      "MCP SDK adapter extension configuration is passive: extensions are accepted but not advertised."
    )
    @extensions_warning_emitted = true
  end
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 55

def alive?
  !@mcp_client.nil?
end

#build_client_extensions_capabilities(protocol_version:) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



86
87
88
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 86

def build_client_extensions_capabilities(protocol_version:) # rubocop:disable Lint/UnusedMethodArgument
  {}
end

#cancelled_notification(reason:, request_id:) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 158

def cancelled_notification(reason:, request_id:)
  return unless @mcp_client&.transport.respond_to?(:native_transport)

  native_transport = @mcp_client.transport.native_transport
  return unless native_transport

  body = RubyLLM::MCP::Native::Messages::Notifications.cancelled(
    request_id: request_id,
    reason: reason
  )
  native_transport.request(body, wait_for_response: false)
end

#capabilitiesObject



64
65
66
67
68
69
70
71
72
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 64

def capabilities
  # Return minimal capabilities for official SDK
  @capabilities ||= ServerCapabilities.new(
    "tools" => {},
    "resources" => {},
    "prompts" => {},
    "logging" => {}
  )
end

#client_capabilitiesObject



74
75
76
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 74

def client_capabilities
  {} # Official SDK handles this internally
end

#execute_prompt(name:, arguments:) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 124

def execute_prompt(name:, arguments:)
  ensure_started
  response = @mcp_client.transport.send_request(
    request: {
      jsonrpc: "2.0",
      id: SecureRandom.uuid,
      method: "prompts/get",
      params: {
        name: name,
        arguments: arguments
      }
    }
  )

  transform_prompt_result(response)
end

#execute_tool(name:, parameters:) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 95

def execute_tool(name:, parameters:)
  ensure_started
  tool = find_tool(name)
  result = @mcp_client.call_tool(tool: tool, arguments: parameters)
  transform_tool_result(result)
rescue RubyLLM::MCP::Errors::TimeoutError => e
  native_transport = @mcp_client&.transport&.native_transport
  if native_transport&.alive? && !e.request_id.nil?
    cancelled_notification(reason: "Request timed out", request_id: e.request_id)
  end
  raise e
end

#extension_modeObject



82
83
84
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 82

def extension_mode
  :passive
end

#pingObject

rubocop:disable Naming/PredicateMethod



59
60
61
62
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 59

def ping # rubocop:disable Naming/PredicateMethod
  ensure_started
  alive?
end

#prompt_list(cursor: nil) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



119
120
121
122
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 119

def prompt_list(cursor: nil) # rubocop:disable Lint/UnusedMethodArgument
  ensure_started
  @mcp_client.prompts.map { |prompt| transform_prompt(prompt) }
end

#resource_list(cursor: nil) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



108
109
110
111
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 108

def resource_list(cursor: nil) # rubocop:disable Lint/UnusedMethodArgument
  ensure_started
  @mcp_client.resources.map { |resource| transform_resource(resource) }
end

#resource_read(uri:) ⇒ Object



113
114
115
116
117
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 113

def resource_read(uri:)
  ensure_started
  result = @mcp_client.read_resource(uri: uri)
  transform_resource_content(result)
end

#resource_template_list(cursor: nil) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



141
142
143
144
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 141

def resource_template_list(cursor: nil) # rubocop:disable Lint/UnusedMethodArgument
  ensure_started
  @mcp_client.resource_templates.map { |resource_template| transform_resource_template(resource_template) }
end

#restart!Object



50
51
52
53
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 50

def restart!
  stop
  start
end

#set_logging(level:) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 146

def set_logging(level:)
  ensure_started
  @mcp_client.transport.send_request(
    request: {
      jsonrpc: "2.0",
      id: SecureRandom.uuid,
      method: "logging/setLevel",
      params: { level: level }
    }
  )
end

#startObject



33
34
35
36
37
38
39
40
41
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 33

def start
  return if @mcp_client

  transport = build_transport
  transport.start if transport.respond_to?(:start)

  @mcp_client = ::MCP::Client.new(transport: transport)
  set_logging(level: client.on_logging_level) if client.logging_handler_enabled?
end

#stopObject



43
44
45
46
47
48
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 43

def stop
  if @mcp_client && @mcp_client.transport.respond_to?(:close)
    @mcp_client.transport.close
  end
  @mcp_client = nil
end

#supports_extension_negotiation?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 78

def supports_extension_negotiation?
  false
end

#tool_list(cursor: nil) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



90
91
92
93
# File 'lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb', line 90

def tool_list(cursor: nil) # rubocop:disable Lint/UnusedMethodArgument
  ensure_started
  @mcp_client.tools.map { |tool| transform_tool(tool) }
end