|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
| 3 | +require "forwardable" |
| 4 | + |
3 | 5 | module RubyLLM |
4 | 6 | module MCP |
5 | 7 | 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 |
7 | 11 |
|
8 | 12 | def initialize(name:, transport_type:, start: true, request_timeout: 8000, config: {}) |
9 | 13 | @name = name |
10 | | - @config = config |
| 14 | + @config = config.merge(request_timeout: request_timeout) |
11 | 15 | @transport_type = transport_type.to_sym |
12 | 16 | @request_timeout = request_timeout |
13 | | - @config[:request_timeout] = request_timeout |
14 | 17 |
|
15 | | - @coordinator = RubyLLM::MCP::Coordinator.new(self, transport_type: @transport_type, config: config) |
| 18 | + @coordinator = Coordinator.new(self, transport_type: @transport_type, config: @config) |
16 | 19 |
|
17 | | - if start |
18 | | - self.start |
19 | | - end |
| 20 | + start_transport if start |
20 | 21 | end |
21 | 22 |
|
22 | | - def capabilities |
23 | | - @coordinator.capabilities |
24 | | - end |
| 23 | + def_delegators :@coordinator, :start_transport, :stop_transport, :restart_transport, :alive?, :capabilities |
25 | 24 |
|
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 |
42 | 28 |
|
43 | 29 | 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 | + |
46 | 35 | @tools.values |
47 | 36 | end |
48 | 37 |
|
49 | 38 | def tool(name, refresh: false) |
50 | | - @tools = nil if refresh |
51 | | - @tools ||= fetch_and_create_tools |
| 39 | + tools(refresh: refresh) |
52 | 40 |
|
53 | 41 | @tools[name] |
54 | 42 | end |
55 | 43 |
|
56 | 44 | 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 | + |
59 | 50 | @resources.values |
60 | 51 | end |
61 | 52 |
|
62 | 53 | def resource(name, refresh: false) |
63 | | - @resources = nil if refresh |
64 | | - @resources ||= fetch_and_create_resources |
| 54 | + resources(refresh: refresh) |
65 | 55 |
|
66 | 56 | @resources[name] |
67 | 57 | end |
68 | 58 |
|
69 | 59 | 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 | + |
72 | 65 | @resource_templates.values |
73 | 66 | end |
74 | 67 |
|
75 | 68 | 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) |
78 | 70 |
|
79 | 71 | @resource_templates[name] |
80 | 72 | end |
81 | 73 |
|
82 | 74 | 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 | + |
85 | 80 | @prompts.values |
86 | 81 | end |
87 | 82 |
|
88 | 83 | def prompt(name, refresh: false) |
89 | | - @prompts = nil if refresh |
90 | | - @prompts ||= fetch_and_create_prompts |
| 84 | + prompts(refresh: refresh) |
91 | 85 |
|
92 | 86 | @prompts[name] |
93 | 87 | end |
94 | 88 |
|
95 | 89 | private |
96 | 90 |
|
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) |
108 | 94 | end |
109 | 95 |
|
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 |
118 | 100 | 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 |
147 | 101 | end |
148 | 102 | end |
149 | 103 | end |
|
0 commit comments