ruby-mcp-client 0.7.3 → 0.8.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/README.md +257 -13
- data/lib/mcp_client/client.rb +225 -6
- data/lib/mcp_client/errors.rb +18 -0
- data/lib/mcp_client/prompt.rb +41 -0
- data/lib/mcp_client/resource.rb +61 -0
- data/lib/mcp_client/resource_content.rb +80 -0
- data/lib/mcp_client/resource_template.rb +57 -0
- data/lib/mcp_client/server_base.rb +55 -0
- data/lib/mcp_client/server_http.rb +142 -0
- data/lib/mcp_client/server_sse/json_rpc_transport.rb +1 -0
- data/lib/mcp_client/server_sse.rb +217 -1
- data/lib/mcp_client/server_stdio/json_rpc_transport.rb +6 -0
- data/lib/mcp_client/server_stdio.rb +182 -0
- data/lib/mcp_client/server_streamable_http.rb +201 -0
- data/lib/mcp_client/version.rb +1 -1
- data/lib/mcp_client.rb +4 -0
- metadata +6 -2
@@ -17,9 +17,11 @@ module MCPClient
|
|
17
17
|
|
18
18
|
include SseParser
|
19
19
|
include JsonRpcTransport
|
20
|
+
|
20
21
|
require_relative 'server_sse/reconnect_monitor'
|
21
22
|
|
22
23
|
include ReconnectMonitor
|
24
|
+
|
23
25
|
# Ratio of close_after timeout to ping interval
|
24
26
|
CLOSE_AFTER_PING_RATIO = 2.5
|
25
27
|
|
@@ -36,7 +38,11 @@ module MCPClient
|
|
36
38
|
# @return [String] The base URL of the MCP server
|
37
39
|
# @!attribute [r] tools
|
38
40
|
# @return [Array<MCPClient::Tool>, nil] List of available tools (nil if not fetched yet)
|
39
|
-
|
41
|
+
# @!attribute [r] prompts
|
42
|
+
# @return [Array<MCPClient::Prompt>, nil] List of available prompts (nil if not fetched yet)
|
43
|
+
# @!attribute [r] resources
|
44
|
+
# @return [Array<MCPClient::Resource>, nil] List of available resources (nil if not fetched yet)
|
45
|
+
attr_reader :base_url, :tools, :prompts, :resources
|
40
46
|
|
41
47
|
# Server information from initialize response
|
42
48
|
# @return [Hash, nil] Server information
|
@@ -104,6 +110,164 @@ module MCPClient
|
|
104
110
|
end
|
105
111
|
end
|
106
112
|
|
113
|
+
# List all prompts available from the MCP server
|
114
|
+
# @return [Array<MCPClient::Prompt>] list of available prompts
|
115
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
116
|
+
# @raise [MCPClient::Errors::TransportError] if response isn't valid JSON
|
117
|
+
# @raise [MCPClient::Errors::PromptGetError] for other errors during prompt listing
|
118
|
+
def list_prompts
|
119
|
+
@mutex.synchronize do
|
120
|
+
return @prompts if @prompts
|
121
|
+
end
|
122
|
+
|
123
|
+
begin
|
124
|
+
ensure_initialized
|
125
|
+
|
126
|
+
prompts_data = request_prompts_list
|
127
|
+
@mutex.synchronize do
|
128
|
+
@prompts = prompts_data.map do |prompt_data|
|
129
|
+
MCPClient::Prompt.from_json(prompt_data, server: self)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
@mutex.synchronize { @prompts }
|
134
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError, MCPClient::Errors::ServerError
|
135
|
+
# Re-raise these errors directly
|
136
|
+
raise
|
137
|
+
rescue StandardError => e
|
138
|
+
raise MCPClient::Errors::PromptGetError, "Error listing prompts: #{e.message}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get a prompt with the given parameters
|
143
|
+
# @param prompt_name [String] the name of the prompt to get
|
144
|
+
# @param parameters [Hash] the parameters to pass to the prompt
|
145
|
+
# @return [Object] the result of the prompt interpolation
|
146
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
147
|
+
# @raise [MCPClient::Errors::TransportError] if response isn't valid JSON
|
148
|
+
# @raise [MCPClient::Errors::PromptGetError] for other errors during prompt interpolation
|
149
|
+
# @raise [MCPClient::Errors::ConnectionError] if server is disconnected
|
150
|
+
def get_prompt(prompt_name, parameters)
|
151
|
+
rpc_request('prompts/get', {
|
152
|
+
name: prompt_name,
|
153
|
+
arguments: parameters
|
154
|
+
})
|
155
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError
|
156
|
+
# Re-raise connection/transport errors directly to match test expectations
|
157
|
+
raise
|
158
|
+
rescue StandardError => e
|
159
|
+
# For all other errors, wrap in PromptGetError
|
160
|
+
raise MCPClient::Errors::PromptGetError, "Error get prompt '#{prompt_name}': #{e.message}"
|
161
|
+
end
|
162
|
+
|
163
|
+
# List all resources available from the MCP server
|
164
|
+
# @param cursor [String, nil] optional cursor for pagination
|
165
|
+
# @return [Hash] result containing resources array and optional nextCursor
|
166
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
167
|
+
# @raise [MCPClient::Errors::TransportError] if response isn't valid JSON
|
168
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during resource listing
|
169
|
+
def list_resources(cursor: nil)
|
170
|
+
@mutex.synchronize do
|
171
|
+
return @resources_result if @resources_result && !cursor
|
172
|
+
end
|
173
|
+
|
174
|
+
begin
|
175
|
+
ensure_initialized
|
176
|
+
|
177
|
+
params = {}
|
178
|
+
params['cursor'] = cursor if cursor
|
179
|
+
result = rpc_request('resources/list', params)
|
180
|
+
|
181
|
+
resources = (result['resources'] || []).map do |resource_data|
|
182
|
+
MCPClient::Resource.from_json(resource_data, server: self)
|
183
|
+
end
|
184
|
+
|
185
|
+
resources_result = { 'resources' => resources, 'nextCursor' => result['nextCursor'] }
|
186
|
+
|
187
|
+
@mutex.synchronize do
|
188
|
+
@resources_result = resources_result unless cursor
|
189
|
+
end
|
190
|
+
|
191
|
+
resources_result
|
192
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError, MCPClient::Errors::ServerError
|
193
|
+
# Re-raise these errors directly
|
194
|
+
raise
|
195
|
+
rescue StandardError => e
|
196
|
+
raise MCPClient::Errors::ResourceReadError, "Error listing resources: #{e.message}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Read a resource by its URI
|
201
|
+
# @param uri [String] the URI of the resource to read
|
202
|
+
# @return [Array<MCPClient::ResourceContent>] array of resource contents
|
203
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
204
|
+
# @raise [MCPClient::Errors::TransportError] if response isn't valid JSON
|
205
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during resource reading
|
206
|
+
# @raise [MCPClient::Errors::ConnectionError] if server is disconnected
|
207
|
+
def read_resource(uri)
|
208
|
+
result = rpc_request('resources/read', { uri: uri })
|
209
|
+
contents = result['contents'] || []
|
210
|
+
contents.map { |content| MCPClient::ResourceContent.from_json(content) }
|
211
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError
|
212
|
+
# Re-raise connection/transport errors directly to match test expectations
|
213
|
+
raise
|
214
|
+
rescue StandardError => e
|
215
|
+
# For all other errors, wrap in ResourceReadError
|
216
|
+
raise MCPClient::Errors::ResourceReadError, "Error reading resource '#{uri}': #{e.message}"
|
217
|
+
end
|
218
|
+
|
219
|
+
# List all resource templates available from the MCP server
|
220
|
+
# @param cursor [String, nil] optional cursor for pagination
|
221
|
+
# @return [Hash] result containing resourceTemplates array and optional nextCursor
|
222
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
223
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during resource template listing
|
224
|
+
def list_resource_templates(cursor: nil)
|
225
|
+
ensure_initialized
|
226
|
+
params = {}
|
227
|
+
params['cursor'] = cursor if cursor
|
228
|
+
result = rpc_request('resources/templates/list', params)
|
229
|
+
|
230
|
+
templates = (result['resourceTemplates'] || []).map do |template_data|
|
231
|
+
MCPClient::ResourceTemplate.from_json(template_data, server: self)
|
232
|
+
end
|
233
|
+
|
234
|
+
{ 'resourceTemplates' => templates, 'nextCursor' => result['nextCursor'] }
|
235
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError, MCPClient::Errors::ServerError
|
236
|
+
raise
|
237
|
+
rescue StandardError => e
|
238
|
+
raise MCPClient::Errors::ResourceReadError, "Error listing resource templates: #{e.message}"
|
239
|
+
end
|
240
|
+
|
241
|
+
# Subscribe to resource updates
|
242
|
+
# @param uri [String] the URI of the resource to subscribe to
|
243
|
+
# @return [Boolean] true if subscription successful
|
244
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
245
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during subscription
|
246
|
+
def subscribe_resource(uri)
|
247
|
+
ensure_initialized
|
248
|
+
rpc_request('resources/subscribe', { uri: uri })
|
249
|
+
true
|
250
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError, MCPClient::Errors::ServerError
|
251
|
+
raise
|
252
|
+
rescue StandardError => e
|
253
|
+
raise MCPClient::Errors::ResourceReadError, "Error subscribing to resource '#{uri}': #{e.message}"
|
254
|
+
end
|
255
|
+
|
256
|
+
# Unsubscribe from resource updates
|
257
|
+
# @param uri [String] the URI of the resource to unsubscribe from
|
258
|
+
# @return [Boolean] true if unsubscription successful
|
259
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
260
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during unsubscription
|
261
|
+
def unsubscribe_resource(uri)
|
262
|
+
ensure_initialized
|
263
|
+
rpc_request('resources/unsubscribe', { uri: uri })
|
264
|
+
true
|
265
|
+
rescue MCPClient::Errors::ConnectionError, MCPClient::Errors::TransportError, MCPClient::Errors::ServerError
|
266
|
+
raise
|
267
|
+
rescue StandardError => e
|
268
|
+
raise MCPClient::Errors::ResourceReadError, "Error unsubscribing from resource '#{uri}': #{e.message}"
|
269
|
+
end
|
270
|
+
|
107
271
|
# List all tools available from the MCP server
|
108
272
|
# @return [Array<MCPClient::Tool>] list of available tools
|
109
273
|
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
@@ -465,6 +629,58 @@ module MCPClient
|
|
465
629
|
raise MCPClient::Errors::ConnectionError, "Authorization failed: #{error_message}"
|
466
630
|
end
|
467
631
|
|
632
|
+
# Request the prompts list using JSON-RPC
|
633
|
+
# @return [Array<Hash>] the prompts data
|
634
|
+
# @raise [MCPClient::Errors::PromptGetError] if prompts list retrieval fails
|
635
|
+
# @private
|
636
|
+
def request_prompts_list
|
637
|
+
@mutex.synchronize do
|
638
|
+
return @prompts_data if @prompts_data
|
639
|
+
end
|
640
|
+
|
641
|
+
result = rpc_request('prompts/list')
|
642
|
+
|
643
|
+
if result && result['prompts']
|
644
|
+
@mutex.synchronize do
|
645
|
+
@prompts_data = result['prompts']
|
646
|
+
end
|
647
|
+
return @mutex.synchronize { @prompts_data.dup }
|
648
|
+
elsif result
|
649
|
+
@mutex.synchronize do
|
650
|
+
@prompts_data = result
|
651
|
+
end
|
652
|
+
return @mutex.synchronize { @prompts_data.dup }
|
653
|
+
end
|
654
|
+
|
655
|
+
raise MCPClient::Errors::PromptGetError, 'Failed to get prompts list from JSON-RPC request'
|
656
|
+
end
|
657
|
+
|
658
|
+
# Request the resources list using JSON-RPC
|
659
|
+
# @return [Array<Hash>] the resources data
|
660
|
+
# @raise [MCPClient::Errors::ResourceReadError] if resources list retrieval fails
|
661
|
+
# @private
|
662
|
+
def request_resources_list
|
663
|
+
@mutex.synchronize do
|
664
|
+
return @resources_data if @resources_data
|
665
|
+
end
|
666
|
+
|
667
|
+
result = rpc_request('resources/list')
|
668
|
+
|
669
|
+
if result && result['resources']
|
670
|
+
@mutex.synchronize do
|
671
|
+
@resources_data = result['resources']
|
672
|
+
end
|
673
|
+
return @mutex.synchronize { @resources_data.dup }
|
674
|
+
elsif result
|
675
|
+
@mutex.synchronize do
|
676
|
+
@resources_data = result
|
677
|
+
end
|
678
|
+
return @mutex.synchronize { @resources_data.dup }
|
679
|
+
end
|
680
|
+
|
681
|
+
raise MCPClient::Errors::ResourceReadError, 'Failed to get resources list from JSON-RPC request'
|
682
|
+
end
|
683
|
+
|
468
684
|
# Request the tools list using JSON-RPC
|
469
685
|
# @return [Array<Hash>] the tools data
|
470
686
|
# @raise [MCPClient::Errors::ToolCallError] if tools list retrieval fails
|
@@ -7,6 +7,7 @@ module MCPClient
|
|
7
7
|
# JSON-RPC request/notification plumbing for stdio transport
|
8
8
|
module JsonRpcTransport
|
9
9
|
include JsonRpcCommon
|
10
|
+
|
10
11
|
# Ensure the server process is started and initialized (handshake)
|
11
12
|
# @return [void]
|
12
13
|
# @raise [MCPClient::Errors::ConnectionError] if initialization fails
|
@@ -33,6 +34,11 @@ module MCPClient
|
|
33
34
|
raise MCPClient::Errors::ConnectionError, "Initialize failed: #{err['message']}"
|
34
35
|
end
|
35
36
|
|
37
|
+
# Store server info and capabilities
|
38
|
+
result = res['result'] || {}
|
39
|
+
@server_info = result['serverInfo']
|
40
|
+
@capabilities = result['capabilities']
|
41
|
+
|
36
42
|
# Send initialized notification
|
37
43
|
notif = build_jsonrpc_notification('notifications/initialized', {})
|
38
44
|
@stdin.puts(notif.to_json)
|
@@ -39,6 +39,8 @@ module MCPClient
|
|
39
39
|
@next_id = 1
|
40
40
|
@pending = {}
|
41
41
|
@initialized = false
|
42
|
+
@server_info = nil
|
43
|
+
@capabilities = nil
|
42
44
|
initialize_logger(logger)
|
43
45
|
@max_retries = retries
|
44
46
|
@retry_backoff = retry_backoff
|
@@ -46,6 +48,14 @@ module MCPClient
|
|
46
48
|
@env = env || {}
|
47
49
|
end
|
48
50
|
|
51
|
+
# Server info from the initialize response
|
52
|
+
# @return [Hash, nil] Server information
|
53
|
+
attr_reader :server_info
|
54
|
+
|
55
|
+
# Server capabilities from the initialize response
|
56
|
+
# @return [Hash, nil] Server capabilities
|
57
|
+
attr_reader :capabilities
|
58
|
+
|
49
59
|
# Connect to the MCP server by launching the command process via stdin/stdout
|
50
60
|
# @return [Boolean] true if connection was successful
|
51
61
|
# @raise [MCPClient::Errors::ConnectionError] if connection fails
|
@@ -102,6 +112,178 @@ module MCPClient
|
|
102
112
|
# Skip non-JSONRPC lines in the output stream
|
103
113
|
end
|
104
114
|
|
115
|
+
# List all prompts available from the MCP server
|
116
|
+
# @return [Array<MCPClient::Prompt>] list of available prompts
|
117
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
118
|
+
# @raise [MCPClient::Errors::PromptGetError] for other errors during prompt listing
|
119
|
+
def list_prompts
|
120
|
+
ensure_initialized
|
121
|
+
req_id = next_id
|
122
|
+
req = { 'jsonrpc' => '2.0', 'id' => req_id, 'method' => 'prompts/list', 'params' => {} }
|
123
|
+
send_request(req)
|
124
|
+
res = wait_response(req_id)
|
125
|
+
if (err = res['error'])
|
126
|
+
raise MCPClient::Errors::ServerError, err['message']
|
127
|
+
end
|
128
|
+
|
129
|
+
(res.dig('result', 'prompts') || []).map { |td| MCPClient::Prompt.from_json(td, server: self) }
|
130
|
+
rescue StandardError => e
|
131
|
+
raise MCPClient::Errors::PromptGetError, "Error listing prompts: #{e.message}"
|
132
|
+
end
|
133
|
+
|
134
|
+
# Get a prompt with the given parameters
|
135
|
+
# @param prompt_name [String] the name of the prompt to get
|
136
|
+
# @param parameters [Hash] the parameters to pass to the prompt
|
137
|
+
# @return [Object] the result of the prompt interpolation
|
138
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
139
|
+
# @raise [MCPClient::Errors::PromptGetError] for other errors during prompt interpolation
|
140
|
+
def get_prompt(prompt_name, parameters)
|
141
|
+
ensure_initialized
|
142
|
+
req_id = next_id
|
143
|
+
# JSON-RPC method for getting a prompt
|
144
|
+
req = {
|
145
|
+
'jsonrpc' => '2.0',
|
146
|
+
'id' => req_id,
|
147
|
+
'method' => 'prompts/get',
|
148
|
+
'params' => { 'name' => prompt_name, 'arguments' => parameters }
|
149
|
+
}
|
150
|
+
send_request(req)
|
151
|
+
res = wait_response(req_id)
|
152
|
+
if (err = res['error'])
|
153
|
+
raise MCPClient::Errors::ServerError, err['message']
|
154
|
+
end
|
155
|
+
|
156
|
+
res['result']
|
157
|
+
rescue StandardError => e
|
158
|
+
raise MCPClient::Errors::PromptGetError, "Error calling prompt '#{prompt_name}': #{e.message}"
|
159
|
+
end
|
160
|
+
|
161
|
+
# List all resources available from the MCP server
|
162
|
+
# @param cursor [String, nil] optional cursor for pagination
|
163
|
+
# @return [Hash] result containing resources array and optional nextCursor
|
164
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
165
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during resource listing
|
166
|
+
def list_resources(cursor: nil)
|
167
|
+
ensure_initialized
|
168
|
+
req_id = next_id
|
169
|
+
params = {}
|
170
|
+
params['cursor'] = cursor if cursor
|
171
|
+
req = { 'jsonrpc' => '2.0', 'id' => req_id, 'method' => 'resources/list', 'params' => params }
|
172
|
+
send_request(req)
|
173
|
+
res = wait_response(req_id)
|
174
|
+
if (err = res['error'])
|
175
|
+
raise MCPClient::Errors::ServerError, err['message']
|
176
|
+
end
|
177
|
+
|
178
|
+
result = res['result'] || {}
|
179
|
+
resources = (result['resources'] || []).map { |td| MCPClient::Resource.from_json(td, server: self) }
|
180
|
+
{ 'resources' => resources, 'nextCursor' => result['nextCursor'] }
|
181
|
+
rescue StandardError => e
|
182
|
+
raise MCPClient::Errors::ResourceReadError, "Error listing resources: #{e.message}"
|
183
|
+
end
|
184
|
+
|
185
|
+
# Read a resource by its URI
|
186
|
+
# @param uri [String] the URI of the resource to read
|
187
|
+
# @return [Array<MCPClient::ResourceContent>] array of resource contents
|
188
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
189
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during resource reading
|
190
|
+
def read_resource(uri)
|
191
|
+
ensure_initialized
|
192
|
+
req_id = next_id
|
193
|
+
# JSON-RPC method for reading a resource
|
194
|
+
req = {
|
195
|
+
'jsonrpc' => '2.0',
|
196
|
+
'id' => req_id,
|
197
|
+
'method' => 'resources/read',
|
198
|
+
'params' => { 'uri' => uri }
|
199
|
+
}
|
200
|
+
send_request(req)
|
201
|
+
res = wait_response(req_id)
|
202
|
+
if (err = res['error'])
|
203
|
+
raise MCPClient::Errors::ServerError, err['message']
|
204
|
+
end
|
205
|
+
|
206
|
+
result = res['result'] || {}
|
207
|
+
contents = result['contents'] || []
|
208
|
+
contents.map { |content| MCPClient::ResourceContent.from_json(content) }
|
209
|
+
rescue StandardError => e
|
210
|
+
raise MCPClient::Errors::ResourceReadError, "Error reading resource '#{uri}': #{e.message}"
|
211
|
+
end
|
212
|
+
|
213
|
+
# List all resource templates available from the MCP server
|
214
|
+
# @param cursor [String, nil] optional cursor for pagination
|
215
|
+
# @return [Hash] result containing resourceTemplates array and optional nextCursor
|
216
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
217
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during resource template listing
|
218
|
+
def list_resource_templates(cursor: nil)
|
219
|
+
ensure_initialized
|
220
|
+
req_id = next_id
|
221
|
+
params = {}
|
222
|
+
params['cursor'] = cursor if cursor
|
223
|
+
req = { 'jsonrpc' => '2.0', 'id' => req_id, 'method' => 'resources/templates/list', 'params' => params }
|
224
|
+
send_request(req)
|
225
|
+
res = wait_response(req_id)
|
226
|
+
if (err = res['error'])
|
227
|
+
raise MCPClient::Errors::ServerError, err['message']
|
228
|
+
end
|
229
|
+
|
230
|
+
result = res['result'] || {}
|
231
|
+
templates = (result['resourceTemplates'] || []).map { |td| MCPClient::ResourceTemplate.from_json(td, server: self) }
|
232
|
+
{ 'resourceTemplates' => templates, 'nextCursor' => result['nextCursor'] }
|
233
|
+
rescue StandardError => e
|
234
|
+
raise MCPClient::Errors::ResourceReadError, "Error listing resource templates: #{e.message}"
|
235
|
+
end
|
236
|
+
|
237
|
+
# Subscribe to resource updates
|
238
|
+
# @param uri [String] the URI of the resource to subscribe to
|
239
|
+
# @return [Boolean] true if subscription successful
|
240
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
241
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during subscription
|
242
|
+
def subscribe_resource(uri)
|
243
|
+
ensure_initialized
|
244
|
+
req_id = next_id
|
245
|
+
req = {
|
246
|
+
'jsonrpc' => '2.0',
|
247
|
+
'id' => req_id,
|
248
|
+
'method' => 'resources/subscribe',
|
249
|
+
'params' => { 'uri' => uri }
|
250
|
+
}
|
251
|
+
send_request(req)
|
252
|
+
res = wait_response(req_id)
|
253
|
+
if (err = res['error'])
|
254
|
+
raise MCPClient::Errors::ServerError, err['message']
|
255
|
+
end
|
256
|
+
|
257
|
+
true
|
258
|
+
rescue StandardError => e
|
259
|
+
raise MCPClient::Errors::ResourceReadError, "Error subscribing to resource '#{uri}': #{e.message}"
|
260
|
+
end
|
261
|
+
|
262
|
+
# Unsubscribe from resource updates
|
263
|
+
# @param uri [String] the URI of the resource to unsubscribe from
|
264
|
+
# @return [Boolean] true if unsubscription successful
|
265
|
+
# @raise [MCPClient::Errors::ServerError] if server returns an error
|
266
|
+
# @raise [MCPClient::Errors::ResourceReadError] for other errors during unsubscription
|
267
|
+
def unsubscribe_resource(uri)
|
268
|
+
ensure_initialized
|
269
|
+
req_id = next_id
|
270
|
+
req = {
|
271
|
+
'jsonrpc' => '2.0',
|
272
|
+
'id' => req_id,
|
273
|
+
'method' => 'resources/unsubscribe',
|
274
|
+
'params' => { 'uri' => uri }
|
275
|
+
}
|
276
|
+
send_request(req)
|
277
|
+
res = wait_response(req_id)
|
278
|
+
if (err = res['error'])
|
279
|
+
raise MCPClient::Errors::ServerError, err['message']
|
280
|
+
end
|
281
|
+
|
282
|
+
true
|
283
|
+
rescue StandardError => e
|
284
|
+
raise MCPClient::Errors::ResourceReadError, "Error unsubscribing from resource '#{uri}': #{e.message}"
|
285
|
+
end
|
286
|
+
|
105
287
|
# List all tools available from the MCP server
|
106
288
|
# @return [Array<MCPClient::Tool>] list of available tools
|
107
289
|
# @raise [MCPClient::Errors::ServerError] if server returns an error
|