Skip to content

Commit 1101d19

Browse files
committed
continue refactor and fully setup client class
1 parent f7fa931 commit 1101d19

File tree

6 files changed

+72
-111
lines changed

6 files changed

+72
-111
lines changed

lib/ruby_llm/mcp/client.rb

Lines changed: 43 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,103 @@
11
# frozen_string_literal: true
22

3+
require "forwardable"
4+
35
module RubyLLM
46
module MCP
57
class Client
6-
attr_reader :name, :config, :transport_type, :transport, :request_timeout, :reverse_proxy_url, :protocol_version
8+
extend Forwardable
9+
10+
attr_reader :name, :config, :transport_type, :request_timeout
711

812
def initialize(name:, transport_type:, start: true, request_timeout: 8000, config: {})
913
@name = name
10-
@config = config
14+
@config = config.merge(request_timeout: request_timeout)
1115
@transport_type = transport_type.to_sym
1216
@request_timeout = request_timeout
13-
@config[:request_timeout] = request_timeout
1417

15-
@coordinator = RubyLLM::MCP::Coordinator.new(self, transport_type: @transport_type, config: config)
18+
@coordinator = Coordinator.new(self, transport_type: @transport_type, config: @config)
1619

17-
if start
18-
self.start
19-
end
20+
start_transport if start
2021
end
2122

22-
def capabilities
23-
@coordinator.capabilities
24-
end
23+
def_delegators :@coordinator, :start_transport, :stop_transport, :restart_transport, :alive?, :capabilities
2524

26-
def start
27-
@coordinator.start_transport
28-
end
29-
30-
def stop
31-
@coordinator.stop_transport
32-
end
33-
34-
def restart!
35-
stop
36-
start
37-
end
38-
39-
def alive?
40-
@coordinator.alive?
41-
end
25+
alias start start_transport
26+
alias stop stop_transport
27+
alias restart! restart_transport
4228

4329
def tools(refresh: false)
44-
@tools = nil if refresh
45-
@tools ||= fetch_and_create_tools
30+
fetch(:tools, refresh) do
31+
tools_data = @coordinator.tool_list.dig("result", "tools")
32+
build_map(tools_data, MCP::Tool)
33+
end
34+
4635
@tools.values
4736
end
4837

4938
def tool(name, refresh: false)
50-
@tools = nil if refresh
51-
@tools ||= fetch_and_create_tools
39+
tools(refresh: refresh)
5240

5341
@tools[name]
5442
end
5543

5644
def resources(refresh: false)
57-
@resources = nil if refresh
58-
@resources ||= fetch_and_create_resources
45+
fetch(:resources, refresh) do
46+
resources_data = @coordinator.resource_list.dig("result", "resources")
47+
build_map(resources_data, MCP::Resource)
48+
end
49+
5950
@resources.values
6051
end
6152

6253
def resource(name, refresh: false)
63-
@resources = nil if refresh
64-
@resources ||= fetch_and_create_resources
54+
resources(refresh: refresh)
6555

6656
@resources[name]
6757
end
6858

6959
def resource_templates(refresh: false)
70-
@resource_templates = nil if refresh
71-
@resource_templates ||= fetch_and_create_resource_templates
60+
fetch(:resource_templates, refresh) do
61+
templates_data = @coordinator.resource_template_list.dig("result", "resourceTemplates")
62+
build_map(templates_data, MCP::ResourceTemplate)
63+
end
64+
7265
@resource_templates.values
7366
end
7467

7568
def resource_template(name, refresh: false)
76-
@resource_templates = nil if refresh
77-
@resource_templates ||= fetch_and_create_resource_templates
69+
resource_templates(refresh: refresh)
7870

7971
@resource_templates[name]
8072
end
8173

8274
def prompts(refresh: false)
83-
@prompts = nil if refresh
84-
@prompts ||= fetch_and_create_prompts
75+
fetch(:prompts, refresh) do
76+
prompts_data = @coordinator.prompt_list.dig("result", "prompts")
77+
build_map(prompts_data, MCP::Prompt)
78+
end
79+
8580
@prompts.values
8681
end
8782

8883
def prompt(name, refresh: false)
89-
@prompts = nil if refresh
90-
@prompts ||= fetch_and_create_prompts
84+
prompts(refresh: refresh)
9185

9286
@prompts[name]
9387
end
9488

9589
private
9690

97-
def fetch_and_create_tools
98-
tools_response = @coordinator.tool_list_request
99-
tools_response = tools_response["result"]["tools"]
100-
101-
tools = {}
102-
tools_response.each do |tool|
103-
new_tool = RubyLLM::MCP::Tool.new(@coordinator, tool)
104-
tools[new_tool.name] = new_tool
105-
end
106-
107-
tools
91+
def fetch(cache_key, refresh)
92+
instance_variable_set("@#{cache_key}", nil) if refresh
93+
instance_variable_get("@#{cache_key}") || instance_variable_set("@#{cache_key}", yield)
10894
end
10995

110-
def fetch_and_create_resources
111-
resources_response = @coordinator.resources_list_request
112-
resources_response = resources_response["result"]["resources"]
113-
114-
resources = {}
115-
resources_response.each do |resource|
116-
new_resource = RubyLLM::MCP::Resource.new(@coordinator, resource)
117-
resources[new_resource.name] = new_resource
96+
def build_map(raw_data, klass)
97+
raw_data.each_with_object({}) do |item, acc|
98+
instance = klass.new(@coordinator, item)
99+
acc[instance.name] = instance
118100
end
119-
120-
resources
121-
end
122-
123-
def fetch_and_create_resource_templates
124-
resource_templates_response = @coordinator.resource_template_list_request
125-
resource_templates_response = resource_templates_response["result"]["resourceTemplates"]
126-
127-
resource_templates = {}
128-
resource_templates_response.each do |resource_template|
129-
new_resource_template = RubyLLM::MCP::ResourceTemplate.new(@coordinator, resource_template)
130-
resource_templates[new_resource_template.name] = new_resource_template
131-
end
132-
133-
resource_templates
134-
end
135-
136-
def fetch_and_create_prompts
137-
prompts_response = @coordinator.prompt_list_request
138-
prompts_response = prompts_response["result"]["prompts"]
139-
140-
prompts = {}
141-
prompts_response.each do |prompt|
142-
new_prompt = RubyLLM::MCP::Prompt.new(@coordinator, prompt)
143-
prompts[new_prompt.name] = new_prompt
144-
end
145-
146-
prompts
147101
end
148102
end
149103
end

lib/ruby_llm/mcp/coordinator.rb

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ def initialize(client, transport_type:, config: {})
1414
@transport_type = transport_type
1515
@config = config
1616

17-
@request_timeout = config[:request_timeout] || 8000
1817
@protocol_version = PROTOCOL_VERSION
1918
@headers = config[:headers] || {}
19+
2020
@transport = nil
2121
@capabilities = nil
2222
end
@@ -28,21 +28,26 @@ def request(body, **options)
2828
def start_transport
2929
case @transport_type
3030
when :sse
31-
@transport = RubyLLM::MCP::Transport::SSE.new(@config[:url], request_timeout: @request_timeout,
32-
headers: @headers)
31+
@transport = RubyLLM::MCP::Transport::SSE.new(@config[:url],
32+
request_timeout: @config[:request_timeout],
33+
headers: @headers)
3334
when :stdio
34-
@transport = RubyLLM::MCP::Transport::Stdio.new(@config[:command], request_timeout: @request_timeout,
35-
args: @config[:args], env: @config[:env])
35+
@transport = RubyLLM::MCP::Transport::Stdio.new(@config[:command],
36+
request_timeout: @config[:request_timeout],
37+
args: @config[:args],
38+
env: @config[:env])
3639
when :streamable
37-
@transport = RubyLLM::MCP::Transport::Streamable.new(@config[:url], request_timeout: @request_timeout,
38-
headers: @headers)
40+
@transport = RubyLLM::MCP::Transport::Streamable.new(@config[:url],
41+
request_timeout: @config[:request_timeout],
42+
headers: @headers)
3943
else
40-
raise "Invalid transport type: #{transport_type}"
44+
message = "Invalid transport type: :#{transport_type}. Supported types are :sse, :stdio, :streamable"
45+
raise Errors::InvalidTransportType.new(message: message)
4146
end
4247

4348
@initialize_response = initialize_request
4449
@capabilities = RubyLLM::MCP::Capabilities.new(@initialize_response["result"]["capabilities"])
45-
notification_request
50+
initialize_notification
4651
end
4752

4853
def stop_transport
@@ -63,7 +68,7 @@ def execute_tool(**args)
6368
RubyLLM::MCP::Requests::ToolCall.new(self, **args).call
6469
end
6570

66-
def resource_read_request(**args)
71+
def resource_read(**args)
6772
RubyLLM::MCP::Requests::ResourceRead.new(self, **args).call
6873
end
6974

@@ -83,23 +88,23 @@ def initialize_request
8388
RubyLLM::MCP::Requests::Initialization.new(self).call
8489
end
8590

86-
def notification_request
87-
RubyLLM::MCP::Requests::Notification.new(self).call
91+
def initialize_notification
92+
RubyLLM::MCP::Requests::InitializeNotification.new(self).call
8893
end
8994

90-
def tool_list_request
95+
def tool_list
9196
RubyLLM::MCP::Requests::ToolList.new(self).call
9297
end
9398

94-
def resources_list_request
99+
def resource_list
95100
RubyLLM::MCP::Requests::ResourceList.new(self).call
96101
end
97102

98-
def resource_template_list_request
103+
def resource_template_list
99104
RubyLLM::MCP::Requests::ResourceTemplateList.new(self).call
100105
end
101106

102-
def prompt_list_request
107+
def prompt_list
103108
RubyLLM::MCP::Requests::PromptList.new(self).call
104109
end
105110
end

lib/ruby_llm/mcp/errors.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ def initialize(message:)
1212
end
1313
end
1414

15+
class CompletionNotAvailable < BaseError; end
16+
17+
class PromptArgumentError < BaseError; end
18+
1519
class InvalidProtocolVersionError < BaseError; end
1620

1721
class SessionExpiredError < BaseError; end
1822

1923
class TimeoutError < BaseError; end
2024

21-
class PromptArgumentError < BaseError; end
22-
23-
class CompletionNotAvailable < BaseError; end
25+
class InvalidTransportType < BaseError; end
2426
end
2527
end
2628
end

lib/ruby_llm/mcp/requests/notification.rb renamed to lib/ruby_llm/mcp/requests/initialize_notification.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22

3-
class RubyLLM::MCP::Requests::Notification < RubyLLM::MCP::Requests::Base
3+
class RubyLLM::MCP::Requests::InitializeNotification < RubyLLM::MCP::Requests::Base
44
def call
55
client.request(notification_body, add_id: false, wait_for_response: false)
66
end

lib/ruby_llm/mcp/resource.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def read_response(uri: @uri)
6464
when "http", "https"
6565
fetch_uri_content(uri)
6666
else # file:// or git://
67-
@read_response ||= @coordinator.resource_read_request(uri: uri)
67+
@read_response ||= @coordinator.resource_read(uri: uri)
6868
end
6969
end
7070

lib/ruby_llm/mcp/resource_template.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def read_response(uri)
5858
when "http", "https"
5959
fetch_uri_content(uri)
6060
else # file:// or git://
61-
@coordinator.resource_read_request(uri: uri)
61+
@coordinator.resource_read(uri: uri)
6262
end
6363
end
6464

0 commit comments

Comments
 (0)