ruby_llm-mcp 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ruby_llm/mcp/client.rb +43 -163
- data/lib/ruby_llm/mcp/coordinator.rb +112 -0
- data/lib/ruby_llm/mcp/errors.rb +5 -3
- data/lib/ruby_llm/mcp/parameter.rb +3 -2
- data/lib/ruby_llm/mcp/prompt.rb +10 -10
- data/lib/ruby_llm/mcp/providers/anthropic/complex_parameter_support.rb +2 -1
- data/lib/ruby_llm/mcp/providers/gemini/complex_parameter_support.rb +2 -1
- data/lib/ruby_llm/mcp/providers/openai/complex_parameter_support.rb +2 -1
- data/lib/ruby_llm/mcp/requests/{notification.rb → initialize_notification.rb} +1 -1
- data/lib/ruby_llm/mcp/resource.rb +4 -4
- data/lib/ruby_llm/mcp/resource_template.rb +7 -7
- data/lib/ruby_llm/mcp/tool.rb +9 -8
- data/lib/ruby_llm/mcp/transport/sse.rb +2 -1
- data/lib/ruby_llm/mcp/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53435b09aee8d32aaede3da3463b8fb782a6d00f6abcabc3b9925f1301cd60c9
|
4
|
+
data.tar.gz: 366cf4d89bef7f79904cd897a7db006ee21a8766c8ff1675e51225576e8c87f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94754a9b6d8799252bebfb19ccc3dedfc6767fdbe07468960ed3eb56978954d734425f79b366fb92610f4c059970a0607c249b3c6c35bca943fbab7e5a721e77
|
7
|
+
data.tar.gz: d0e757cdc7d064ffc4334b0b1c2a1ebc1feff3dc684d35d28f0b51ce5761a3c80ad427654767fae97da8c8ddacbc182d3d896ef4dedc45ce4d6d8e6297b81fe3
|
data/lib/ruby_llm/mcp/client.rb
CHANGED
@@ -1,223 +1,103 @@
|
|
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
|
-
|
7
|
-
PV_2024_11_05 = "2024-11-05"
|
8
|
+
extend Forwardable
|
8
9
|
|
9
|
-
attr_reader :name, :config, :transport_type, :
|
10
|
-
:capabilities
|
10
|
+
attr_reader :name, :config, :transport_type, :request_timeout
|
11
11
|
|
12
|
-
def initialize(name:, transport_type:, start: true, request_timeout: 8000,
|
12
|
+
def initialize(name:, transport_type:, start: true, request_timeout: 8000, config: {})
|
13
13
|
@name = name
|
14
|
-
@config = config
|
15
|
-
@protocol_version = PROTOCOL_VERSION
|
16
|
-
@headers = config[:headers] || {}
|
17
|
-
|
14
|
+
@config = config.merge(request_timeout: request_timeout)
|
18
15
|
@transport_type = transport_type.to_sym
|
19
|
-
@transport = nil
|
20
|
-
|
21
|
-
@capabilities = nil
|
22
|
-
|
23
16
|
@request_timeout = request_timeout
|
24
|
-
@reverse_proxy_url = reverse_proxy_url
|
25
17
|
|
26
|
-
|
27
|
-
self.start
|
28
|
-
end
|
29
|
-
end
|
18
|
+
@coordinator = Coordinator.new(self, transport_type: @transport_type, config: @config)
|
30
19
|
|
31
|
-
|
32
|
-
@transport.request(body, **options)
|
20
|
+
start_transport if start
|
33
21
|
end
|
34
22
|
|
35
|
-
|
36
|
-
case @transport_type
|
37
|
-
when :sse
|
38
|
-
@transport = RubyLLM::MCP::Transport::SSE.new(@config[:url], request_timeout: @request_timeout,
|
39
|
-
headers: @headers)
|
40
|
-
when :stdio
|
41
|
-
@transport = RubyLLM::MCP::Transport::Stdio.new(@config[:command], request_timeout: @request_timeout,
|
42
|
-
args: @config[:args], env: @config[:env])
|
43
|
-
when :streamable
|
44
|
-
@transport = RubyLLM::MCP::Transport::Streamable.new(@config[:url], request_timeout: @request_timeout,
|
45
|
-
headers: @headers)
|
46
|
-
else
|
47
|
-
raise "Invalid transport type: #{transport_type}"
|
48
|
-
end
|
23
|
+
def_delegators :@coordinator, :start_transport, :stop_transport, :restart_transport, :alive?, :capabilities
|
49
24
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def stop
|
56
|
-
@transport&.close
|
57
|
-
@transport = nil
|
58
|
-
end
|
59
|
-
|
60
|
-
def restart!
|
61
|
-
stop
|
62
|
-
start
|
63
|
-
end
|
64
|
-
|
65
|
-
def alive?
|
66
|
-
!!@transport&.alive?
|
67
|
-
end
|
25
|
+
alias start start_transport
|
26
|
+
alias stop stop_transport
|
27
|
+
alias restart! restart_transport
|
68
28
|
|
69
29
|
def tools(refresh: false)
|
70
|
-
|
71
|
-
|
30
|
+
fetch(:tools, refresh) do
|
31
|
+
tools_data = @coordinator.tool_list.dig("result", "tools")
|
32
|
+
build_map(tools_data, MCP::Tool)
|
33
|
+
end
|
34
|
+
|
72
35
|
@tools.values
|
73
36
|
end
|
74
37
|
|
75
38
|
def tool(name, refresh: false)
|
76
|
-
|
77
|
-
@tools ||= fetch_and_create_tools
|
39
|
+
tools(refresh: refresh)
|
78
40
|
|
79
41
|
@tools[name]
|
80
42
|
end
|
81
43
|
|
82
44
|
def resources(refresh: false)
|
83
|
-
|
84
|
-
|
45
|
+
fetch(:resources, refresh) do
|
46
|
+
resources_data = @coordinator.resource_list.dig("result", "resources")
|
47
|
+
build_map(resources_data, MCP::Resource)
|
48
|
+
end
|
49
|
+
|
85
50
|
@resources.values
|
86
51
|
end
|
87
52
|
|
88
53
|
def resource(name, refresh: false)
|
89
|
-
|
90
|
-
@resources ||= fetch_and_create_resources
|
54
|
+
resources(refresh: refresh)
|
91
55
|
|
92
56
|
@resources[name]
|
93
57
|
end
|
94
58
|
|
95
59
|
def resource_templates(refresh: false)
|
96
|
-
|
97
|
-
|
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
|
+
|
98
65
|
@resource_templates.values
|
99
66
|
end
|
100
67
|
|
101
68
|
def resource_template(name, refresh: false)
|
102
|
-
|
103
|
-
@resource_templates ||= fetch_and_create_resource_templates
|
69
|
+
resource_templates(refresh: refresh)
|
104
70
|
|
105
71
|
@resource_templates[name]
|
106
72
|
end
|
107
73
|
|
108
74
|
def prompts(refresh: false)
|
109
|
-
|
110
|
-
|
75
|
+
fetch(:prompts, refresh) do
|
76
|
+
prompts_data = @coordinator.prompt_list.dig("result", "prompts")
|
77
|
+
build_map(prompts_data, MCP::Prompt)
|
78
|
+
end
|
79
|
+
|
111
80
|
@prompts.values
|
112
81
|
end
|
113
82
|
|
114
83
|
def prompt(name, refresh: false)
|
115
|
-
|
116
|
-
@prompts ||= fetch_and_create_prompts
|
84
|
+
prompts(refresh: refresh)
|
117
85
|
|
118
86
|
@prompts[name]
|
119
87
|
end
|
120
88
|
|
121
|
-
def execute_tool(**args)
|
122
|
-
RubyLLM::MCP::Requests::ToolCall.new(self, **args).call
|
123
|
-
end
|
124
|
-
|
125
|
-
def resource_read_request(**args)
|
126
|
-
RubyLLM::MCP::Requests::ResourceRead.new(self, **args).call
|
127
|
-
end
|
128
|
-
|
129
|
-
def completion_resource(**args)
|
130
|
-
RubyLLM::MCP::Requests::CompletionResource.new(self, **args).call
|
131
|
-
end
|
132
|
-
|
133
|
-
def completion_prompt(**args)
|
134
|
-
RubyLLM::MCP::Requests::CompletionPrompt.new(self, **args).call
|
135
|
-
end
|
136
|
-
|
137
|
-
def execute_prompt(**args)
|
138
|
-
RubyLLM::MCP::Requests::PromptCall.new(self, **args).call
|
139
|
-
end
|
140
|
-
|
141
89
|
private
|
142
90
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
def notification_request
|
148
|
-
RubyLLM::MCP::Requests::Notification.new(self).call
|
149
|
-
end
|
150
|
-
|
151
|
-
def tool_list_request
|
152
|
-
RubyLLM::MCP::Requests::ToolList.new(self).call
|
153
|
-
end
|
154
|
-
|
155
|
-
def resources_list_request
|
156
|
-
RubyLLM::MCP::Requests::ResourceList.new(self).call
|
157
|
-
end
|
158
|
-
|
159
|
-
def resource_template_list_request
|
160
|
-
RubyLLM::MCP::Requests::ResourceTemplateList.new(self).call
|
161
|
-
end
|
162
|
-
|
163
|
-
def prompt_list_request
|
164
|
-
RubyLLM::MCP::Requests::PromptList.new(self).call
|
165
|
-
end
|
166
|
-
|
167
|
-
def fetch_and_create_tools
|
168
|
-
tools_response = tool_list_request
|
169
|
-
tools_response = tools_response["result"]["tools"]
|
170
|
-
|
171
|
-
tools = {}
|
172
|
-
tools_response.each do |tool|
|
173
|
-
new_tool = RubyLLM::MCP::Tool.new(self, tool)
|
174
|
-
tools[new_tool.name] = new_tool
|
175
|
-
end
|
176
|
-
|
177
|
-
tools
|
178
|
-
end
|
179
|
-
|
180
|
-
def fetch_and_create_resources
|
181
|
-
resources_response = resources_list_request
|
182
|
-
resources_response = resources_response["result"]["resources"]
|
183
|
-
|
184
|
-
resources = {}
|
185
|
-
resources_response.each do |resource|
|
186
|
-
new_resource = RubyLLM::MCP::Resource.new(self, resource)
|
187
|
-
resources[new_resource.name] = new_resource
|
188
|
-
end
|
189
|
-
|
190
|
-
resources
|
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)
|
191
94
|
end
|
192
95
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
resource_templates = {}
|
198
|
-
resource_templates_response.each do |resource_template|
|
199
|
-
new_resource_template = RubyLLM::MCP::ResourceTemplate.new(self, resource_template)
|
200
|
-
resource_templates[new_resource_template.name] = new_resource_template
|
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
|
201
100
|
end
|
202
|
-
|
203
|
-
resource_templates
|
204
|
-
end
|
205
|
-
|
206
|
-
def fetch_and_create_prompts
|
207
|
-
prompts_response = prompt_list_request
|
208
|
-
prompts_response = prompts_response["result"]["prompts"]
|
209
|
-
|
210
|
-
prompts = {}
|
211
|
-
prompts_response.each do |prompt|
|
212
|
-
new_prompt = RubyLLM::MCP::Prompt.new(self,
|
213
|
-
name: prompt["name"],
|
214
|
-
description: prompt["description"],
|
215
|
-
arguments: prompt["arguments"])
|
216
|
-
|
217
|
-
prompts[new_prompt.name] = new_prompt
|
218
|
-
end
|
219
|
-
|
220
|
-
prompts
|
221
101
|
end
|
222
102
|
end
|
223
103
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
class Coordinator
|
6
|
+
PROTOCOL_VERSION = "2025-03-26"
|
7
|
+
PV_2024_11_05 = "2024-11-05"
|
8
|
+
|
9
|
+
attr_reader :client, :transport_type, :config, :request_timeout, :headers, :transport, :initialize_response,
|
10
|
+
:capabilities, :protocol_version
|
11
|
+
|
12
|
+
def initialize(client, transport_type:, config: {})
|
13
|
+
@client = client
|
14
|
+
@transport_type = transport_type
|
15
|
+
@config = config
|
16
|
+
|
17
|
+
@protocol_version = PROTOCOL_VERSION
|
18
|
+
@headers = config[:headers] || {}
|
19
|
+
|
20
|
+
@transport = nil
|
21
|
+
@capabilities = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def request(body, **options)
|
25
|
+
@transport.request(body, **options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def start_transport
|
29
|
+
case @transport_type
|
30
|
+
when :sse
|
31
|
+
@transport = RubyLLM::MCP::Transport::SSE.new(@config[:url],
|
32
|
+
request_timeout: @config[:request_timeout],
|
33
|
+
headers: @headers)
|
34
|
+
when :stdio
|
35
|
+
@transport = RubyLLM::MCP::Transport::Stdio.new(@config[:command],
|
36
|
+
request_timeout: @config[:request_timeout],
|
37
|
+
args: @config[:args],
|
38
|
+
env: @config[:env])
|
39
|
+
when :streamable
|
40
|
+
@transport = RubyLLM::MCP::Transport::Streamable.new(@config[:url],
|
41
|
+
request_timeout: @config[:request_timeout],
|
42
|
+
headers: @headers)
|
43
|
+
else
|
44
|
+
message = "Invalid transport type: :#{transport_type}. Supported types are :sse, :stdio, :streamable"
|
45
|
+
raise Errors::InvalidTransportType.new(message: message)
|
46
|
+
end
|
47
|
+
|
48
|
+
@initialize_response = initialize_request
|
49
|
+
@capabilities = RubyLLM::MCP::Capabilities.new(@initialize_response["result"]["capabilities"])
|
50
|
+
initialize_notification
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop_transport
|
54
|
+
@transport&.close
|
55
|
+
@transport = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def restart_transport
|
59
|
+
stop_transport
|
60
|
+
start_transport
|
61
|
+
end
|
62
|
+
|
63
|
+
def alive?
|
64
|
+
!!@transport&.alive?
|
65
|
+
end
|
66
|
+
|
67
|
+
def execute_tool(**args)
|
68
|
+
RubyLLM::MCP::Requests::ToolCall.new(self, **args).call
|
69
|
+
end
|
70
|
+
|
71
|
+
def resource_read(**args)
|
72
|
+
RubyLLM::MCP::Requests::ResourceRead.new(self, **args).call
|
73
|
+
end
|
74
|
+
|
75
|
+
def completion_resource(**args)
|
76
|
+
RubyLLM::MCP::Requests::CompletionResource.new(self, **args).call
|
77
|
+
end
|
78
|
+
|
79
|
+
def completion_prompt(**args)
|
80
|
+
RubyLLM::MCP::Requests::CompletionPrompt.new(self, **args).call
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute_prompt(**args)
|
84
|
+
RubyLLM::MCP::Requests::PromptCall.new(self, **args).call
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize_request
|
88
|
+
RubyLLM::MCP::Requests::Initialization.new(self).call
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize_notification
|
92
|
+
RubyLLM::MCP::Requests::InitializeNotification.new(self).call
|
93
|
+
end
|
94
|
+
|
95
|
+
def tool_list
|
96
|
+
RubyLLM::MCP::Requests::ToolList.new(self).call
|
97
|
+
end
|
98
|
+
|
99
|
+
def resource_list
|
100
|
+
RubyLLM::MCP::Requests::ResourceList.new(self).call
|
101
|
+
end
|
102
|
+
|
103
|
+
def resource_template_list
|
104
|
+
RubyLLM::MCP::Requests::ResourceTemplateList.new(self).call
|
105
|
+
end
|
106
|
+
|
107
|
+
def prompt_list
|
108
|
+
RubyLLM::MCP::Requests::PromptList.new(self).call
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/ruby_llm/mcp/errors.rb
CHANGED
@@ -12,15 +12,17 @@ module RubyLLM
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
class CompletionNotAvailable < BaseError; end
|
16
|
+
|
17
|
+
class PromptArgumentError < BaseError; end
|
18
|
+
|
15
19
|
class InvalidProtocolVersionError < BaseError; end
|
16
20
|
|
17
21
|
class SessionExpiredError < BaseError; end
|
18
22
|
|
19
23
|
class TimeoutError < BaseError; end
|
20
24
|
|
21
|
-
class
|
22
|
-
|
23
|
-
class CompletionNotAvailable < BaseError; end
|
25
|
+
class InvalidTransportType < BaseError; end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -3,12 +3,13 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module MCP
|
5
5
|
class Parameter < RubyLLM::Parameter
|
6
|
-
attr_accessor :items, :properties, :enum, :union_type
|
6
|
+
attr_accessor :items, :properties, :enum, :union_type, :default
|
7
7
|
|
8
|
-
def initialize(name, type: "string", desc: nil, required: true, union_type: nil)
|
8
|
+
def initialize(name, type: "string", desc: nil, required: true, default: nil, union_type: nil) # rubocop:disable Metrics/ParameterLists
|
9
9
|
super(name, type: type.to_sym, desc: desc, required: required)
|
10
10
|
@properties = {}
|
11
11
|
@union_type = union_type
|
12
|
+
@default = default
|
12
13
|
end
|
13
14
|
|
14
15
|
def item_type
|
data/lib/ruby_llm/mcp/prompt.rb
CHANGED
@@ -13,13 +13,13 @@ module RubyLLM
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
attr_reader :name, :description, :arguments, :
|
16
|
+
attr_reader :name, :description, :arguments, :coordinator
|
17
17
|
|
18
|
-
def initialize(
|
19
|
-
@
|
20
|
-
@name = name
|
21
|
-
@description = description
|
22
|
-
@arguments = parse_arguments(arguments)
|
18
|
+
def initialize(coordinator, prompt)
|
19
|
+
@coordinator = coordinator
|
20
|
+
@name = prompt["name"]
|
21
|
+
@description = prompt["description"]
|
22
|
+
@arguments = parse_arguments(prompt["arguments"])
|
23
23
|
end
|
24
24
|
|
25
25
|
def fetch(arguments = {})
|
@@ -43,8 +43,8 @@ module RubyLLM
|
|
43
43
|
alias say ask
|
44
44
|
|
45
45
|
def complete(argument, value)
|
46
|
-
if @
|
47
|
-
response = @
|
46
|
+
if @coordinator.capabilities.completion?
|
47
|
+
response = @coordinator.completion_prompt(name: @name, argument: argument, value: value)
|
48
48
|
response = response.dig("result", "completion")
|
49
49
|
|
50
50
|
Completion.new(values: response["values"], total: response["total"], has_more: response["hasMore"])
|
@@ -56,7 +56,7 @@ module RubyLLM
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def fetch_prompt_messages(arguments)
|
59
|
-
response = @
|
59
|
+
response = @coordinator.execute_prompt(
|
60
60
|
name: @name,
|
61
61
|
arguments: arguments
|
62
62
|
)
|
@@ -87,7 +87,7 @@ module RubyLLM
|
|
87
87
|
attachment = MCP::Attachment.new(content["content"], content["mime_type"])
|
88
88
|
MCP::Content.new(text: nil, attachments: [attachment])
|
89
89
|
when "resource"
|
90
|
-
resource = Resource.new(
|
90
|
+
resource = Resource.new(coordinator, content["resource"])
|
91
91
|
resource.to_content
|
92
92
|
end
|
93
93
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class RubyLLM::MCP::Requests::
|
3
|
+
class RubyLLM::MCP::Requests::InitializeNotification < RubyLLM::MCP::Requests::Base
|
4
4
|
def call
|
5
5
|
client.request(notification_body, add_id: false, wait_for_response: false)
|
6
6
|
end
|
@@ -3,10 +3,10 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module MCP
|
5
5
|
class Resource
|
6
|
-
attr_reader :uri, :name, :description, :mime_type, :
|
6
|
+
attr_reader :uri, :name, :description, :mime_type, :coordinator
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
8
|
+
def initialize(coordinator, resource)
|
9
|
+
@coordinator = coordinator
|
10
10
|
@uri = resource["uri"]
|
11
11
|
@name = resource["name"]
|
12
12
|
@description = resource["description"]
|
@@ -64,7 +64,7 @@ module RubyLLM
|
|
64
64
|
when "http", "https"
|
65
65
|
fetch_uri_content(uri)
|
66
66
|
else # file:// or git://
|
67
|
-
@read_response ||= @
|
67
|
+
@read_response ||= @coordinator.resource_read(uri: uri)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -3,10 +3,10 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module MCP
|
5
5
|
class ResourceTemplate
|
6
|
-
attr_reader :uri, :name, :description, :mime_type, :
|
6
|
+
attr_reader :uri, :name, :description, :mime_type, :coordinator, :template
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
8
|
+
def initialize(coordinator, resource)
|
9
|
+
@coordinator = coordinator
|
10
10
|
@uri = resource["uriTemplate"]
|
11
11
|
@name = resource["name"]
|
12
12
|
@description = resource["description"]
|
@@ -18,7 +18,7 @@ module RubyLLM
|
|
18
18
|
response = read_response(uri)
|
19
19
|
content_response = response.dig("result", "contents", 0)
|
20
20
|
|
21
|
-
Resource.new(
|
21
|
+
Resource.new(coordinator, {
|
22
22
|
"uri" => uri,
|
23
23
|
"name" => "#{@name} (#{uri})",
|
24
24
|
"description" => @description,
|
@@ -32,8 +32,8 @@ module RubyLLM
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def complete(argument, value)
|
35
|
-
if @
|
36
|
-
response = @
|
35
|
+
if @coordinator.capabilities.completion?
|
36
|
+
response = @coordinator.completion_resource(uri: @uri, argument: argument, value: value)
|
37
37
|
response = response.dig("result", "completion")
|
38
38
|
|
39
39
|
Completion.new(values: response["values"], total: response["total"], has_more: response["hasMore"])
|
@@ -58,7 +58,7 @@ module RubyLLM
|
|
58
58
|
when "http", "https"
|
59
59
|
fetch_uri_content(uri)
|
60
60
|
else # file:// or git://
|
61
|
-
@
|
61
|
+
@coordinator.resource_read(uri: uri)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
data/lib/ruby_llm/mcp/tool.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module MCP
|
5
5
|
class Tool < RubyLLM::Tool
|
6
|
-
attr_reader :name, :description, :parameters, :
|
6
|
+
attr_reader :name, :description, :parameters, :coordinator, :tool_response
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(coordinator, tool_response)
|
9
9
|
super()
|
10
|
-
@
|
10
|
+
@coordinator = coordinator
|
11
11
|
|
12
12
|
@name = tool_response["name"]
|
13
13
|
@description = tool_response["description"].to_s
|
@@ -15,7 +15,7 @@ module RubyLLM
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def execute(**params)
|
18
|
-
response = @
|
18
|
+
response = @coordinator.execute_tool(
|
19
19
|
name: @name,
|
20
20
|
parameters: params
|
21
21
|
)
|
@@ -70,7 +70,8 @@ module RubyLLM
|
|
70
70
|
key,
|
71
71
|
type: param_data["type"] || lifted_type,
|
72
72
|
desc: param_data["description"],
|
73
|
-
required: param_data["required"]
|
73
|
+
required: param_data["required"],
|
74
|
+
default: param_data["default"]
|
74
75
|
)
|
75
76
|
|
76
77
|
if param.type == :array
|
@@ -79,8 +80,8 @@ module RubyLLM
|
|
79
80
|
if items.key?("properties")
|
80
81
|
param.properties = create_parameters(items)
|
81
82
|
end
|
82
|
-
if
|
83
|
-
param.enum =
|
83
|
+
if items.key?("enum")
|
84
|
+
param.enum = items["enum"]
|
84
85
|
end
|
85
86
|
elsif param.type == :object
|
86
87
|
if param_data.key?("properties")
|
@@ -106,7 +107,7 @@ module RubyLLM
|
|
106
107
|
"content" => content["resource"]
|
107
108
|
}
|
108
109
|
|
109
|
-
resource = Resource.new(
|
110
|
+
resource = Resource.new(coordinator, resource_data)
|
110
111
|
resource.to_content
|
111
112
|
end
|
112
113
|
end
|
@@ -12,9 +12,10 @@ module RubyLLM
|
|
12
12
|
class SSE
|
13
13
|
attr_reader :headers, :id
|
14
14
|
|
15
|
-
def initialize(url, headers: {})
|
15
|
+
def initialize(url, headers: {}, request_timeout: 8000)
|
16
16
|
@event_url = url
|
17
17
|
@messages_url = nil
|
18
|
+
@request_timeout = request_timeout
|
18
19
|
|
19
20
|
uri = URI.parse(url)
|
20
21
|
@root_url = "#{uri.scheme}://#{uri.host}"
|
data/lib/ruby_llm/mcp/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_llm-mcp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrick Vice
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/ruby_llm/mcp/client.rb
|
115
115
|
- lib/ruby_llm/mcp/completion.rb
|
116
116
|
- lib/ruby_llm/mcp/content.rb
|
117
|
+
- lib/ruby_llm/mcp/coordinator.rb
|
117
118
|
- lib/ruby_llm/mcp/errors.rb
|
118
119
|
- lib/ruby_llm/mcp/parameter.rb
|
119
120
|
- lib/ruby_llm/mcp/prompt.rb
|
@@ -124,7 +125,7 @@ files:
|
|
124
125
|
- lib/ruby_llm/mcp/requests/completion_prompt.rb
|
125
126
|
- lib/ruby_llm/mcp/requests/completion_resource.rb
|
126
127
|
- lib/ruby_llm/mcp/requests/initialization.rb
|
127
|
-
- lib/ruby_llm/mcp/requests/
|
128
|
+
- lib/ruby_llm/mcp/requests/initialize_notification.rb
|
128
129
|
- lib/ruby_llm/mcp/requests/prompt_call.rb
|
129
130
|
- lib/ruby_llm/mcp/requests/prompt_list.rb
|
130
131
|
- lib/ruby_llm/mcp/requests/resource_list.rb
|