ruby-mcp-client 0.4.0 → 0.4.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 +8 -0
- data/lib/mcp_client/client.rb +22 -0
- data/lib/mcp_client/server_base.rb +7 -0
- data/lib/mcp_client/server_sse.rb +26 -19
- data/lib/mcp_client/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87cf7d5701adff89363dd653d263189bded230a32232bbbb496d41b4092afdec
|
4
|
+
data.tar.gz: 9a3561d2f97f0ef518cf75dfce82a8521140c9cdc381aaef3f3b3658265a7298
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 361f1916a531f14ded3292e15ea4947b0e3715d71d2be71dd24ea198616a82c55e516379a59a455b6c14f9fb7b3b9a1d02bdd457fde0b975ece6320bbb6da23b
|
7
|
+
data.tar.gz: b38270ec5a9ddce3a3689e6c8fb6e86efaa35453b6bcc8a590612d02fe1ef86040d62fd30fb95ff02640bb7b822bf8f473ae24fd571ad696069657f98a9d5c20
|
data/README.md
CHANGED
@@ -99,6 +99,11 @@ client.send_rpc('custom_method', params: { key: 'value' }, server: :sse) # Uses
|
|
99
99
|
result = client.send_rpc('another_method', params: { data: 123 }) # Uses first available server
|
100
100
|
client.send_notification('status_update', params: { status: 'ready' })
|
101
101
|
|
102
|
+
# Check server connectivity
|
103
|
+
client.ping # Basic connectivity check
|
104
|
+
client.ping({ echo: "hello" }) # With optional parameters
|
105
|
+
client.ping({}, server_index: 1) # Ping a specific server by index
|
106
|
+
|
102
107
|
# Clear cached tools to force fresh fetch on next list
|
103
108
|
client.clear_cache
|
104
109
|
# Clean up connections
|
@@ -215,6 +220,9 @@ The SSE client implementation provides these key features:
|
|
215
220
|
- **Custom RPC methods**: Send any custom JSON-RPC method or notification through `send_rpc` and `send_notification`
|
216
221
|
- **Configurable retries**: All RPC requests support configurable retries with exponential backoff
|
217
222
|
- **Consistent logging**: Tagged, leveled logging across all components for better debugging
|
223
|
+
- **Graceful fallbacks**: Automatic fallback to synchronous HTTP when SSE connection fails
|
224
|
+
- **URL normalization**: Consistent URL handling that respects user-provided formats
|
225
|
+
- **Server connectivity check**: Built-in `ping` method to test server connectivity and health
|
218
226
|
|
219
227
|
## Requirements
|
220
228
|
|
data/lib/mcp_client/client.rb
CHANGED
@@ -163,6 +163,28 @@ module MCPClient
|
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
166
|
+
# Ping the MCP server to check connectivity
|
167
|
+
# @param params [Hash] optional parameters for the ping request
|
168
|
+
# @param server_index [Integer, nil] optional index of a specific server to ping, nil for first available
|
169
|
+
# @return [Object] result from the ping request
|
170
|
+
# @raise [MCPClient::Errors::ServerNotFound] if no server is available
|
171
|
+
def ping(params = {}, server_index: nil)
|
172
|
+
if server_index.nil?
|
173
|
+
# Ping first available server
|
174
|
+
raise MCPClient::Errors::ServerNotFound, 'No server available for ping' if @servers.empty?
|
175
|
+
|
176
|
+
@servers.first.ping(params)
|
177
|
+
else
|
178
|
+
# Ping specified server
|
179
|
+
if server_index >= @servers.length
|
180
|
+
raise MCPClient::Errors::ServerNotFound,
|
181
|
+
"Server at index #{server_index} not found"
|
182
|
+
end
|
183
|
+
|
184
|
+
@servers[server_index].ping(params)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
166
188
|
private
|
167
189
|
|
168
190
|
# Validate parameters against tool JSON schema (checks required properties)
|
@@ -45,6 +45,13 @@ module MCPClient
|
|
45
45
|
raise NotImplementedError, 'Subclasses must implement rpc_notify'
|
46
46
|
end
|
47
47
|
|
48
|
+
# Ping the MCP server to check connectivity
|
49
|
+
# @param params [Hash] optional parameters for the ping request
|
50
|
+
# @return [Object] result from the ping request
|
51
|
+
def ping(params = {})
|
52
|
+
rpc_request('ping', params)
|
53
|
+
end
|
54
|
+
|
48
55
|
# Register a callback to receive JSON-RPC notifications
|
49
56
|
# @yield [method, params] invoked when a notification is received
|
50
57
|
# @return [void]
|
@@ -26,7 +26,8 @@ module MCPClient
|
|
26
26
|
@logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
|
27
27
|
@max_retries = retries
|
28
28
|
@retry_backoff = retry_backoff
|
29
|
-
|
29
|
+
# Normalize base_url: strip any trailing slash, use exactly as provided
|
30
|
+
@base_url = base_url.chomp('/')
|
30
31
|
@headers = headers.merge({
|
31
32
|
'Accept' => 'text/event-stream',
|
32
33
|
'Cache-Control' => 'no-cache',
|
@@ -45,6 +46,8 @@ module MCPClient
|
|
45
46
|
@connection_established = false
|
46
47
|
@connection_cv = @mutex.new_cond
|
47
48
|
@initialized = false
|
49
|
+
# Whether to use SSE transport; may disable if handshake fails
|
50
|
+
@use_sse = true
|
48
51
|
end
|
49
52
|
|
50
53
|
# Stream tool call fallback for SSE transport (yields single result)
|
@@ -230,12 +233,13 @@ module MCPClient
|
|
230
233
|
|
231
234
|
private
|
232
235
|
|
233
|
-
# Ensure
|
236
|
+
# Ensure SSE initialization handshake has been performed
|
234
237
|
def ensure_initialized
|
235
238
|
return if @initialized
|
236
239
|
|
237
240
|
connect
|
238
241
|
perform_initialize
|
242
|
+
|
239
243
|
@initialized = true
|
240
244
|
end
|
241
245
|
|
@@ -286,6 +290,7 @@ module MCPClient
|
|
286
290
|
http.request(request) do |response|
|
287
291
|
unless response.is_a?(Net::HTTPSuccess) && response['content-type']&.start_with?('text/event-stream')
|
288
292
|
@mutex.synchronize do
|
293
|
+
# Signal connection attempt completed (failed)
|
289
294
|
@connection_established = false
|
290
295
|
@connection_cv.broadcast
|
291
296
|
end
|
@@ -293,7 +298,10 @@ module MCPClient
|
|
293
298
|
end
|
294
299
|
|
295
300
|
@mutex.synchronize do
|
301
|
+
# Signal connection established and SSE ready
|
296
302
|
@sse_connected = true
|
303
|
+
@connection_established = true
|
304
|
+
@connection_cv.broadcast
|
297
305
|
end
|
298
306
|
|
299
307
|
response.read_body do |chunk|
|
@@ -303,6 +311,11 @@ module MCPClient
|
|
303
311
|
end
|
304
312
|
end
|
305
313
|
rescue StandardError
|
314
|
+
# On any SSE thread error, signal connection as established to unblock connect
|
315
|
+
@mutex.synchronize do
|
316
|
+
@connection_established = true
|
317
|
+
@connection_cv.broadcast
|
318
|
+
end
|
306
319
|
nil
|
307
320
|
ensure
|
308
321
|
sse_http&.finish if sse_http&.started?
|
@@ -499,37 +512,31 @@ module MCPClient
|
|
499
512
|
raise MCPClient::Errors::ServerError, "Server returned error: #{response.code} #{response.message}"
|
500
513
|
end
|
501
514
|
|
502
|
-
|
515
|
+
# If SSE transport is enabled, retrieve the result via the SSE channel
|
516
|
+
if @use_sse
|
503
517
|
request_id = request[:id]
|
504
|
-
|
505
518
|
start_time = Time.now
|
506
|
-
timeout = 10
|
519
|
+
timeout = @read_timeout || 10
|
507
520
|
result = nil
|
508
521
|
|
509
522
|
loop do
|
510
523
|
@mutex.synchronize do
|
511
|
-
if @sse_results
|
512
|
-
result = @sse_results[request_id]
|
513
|
-
@sse_results.delete(request_id)
|
514
|
-
end
|
524
|
+
result = @sse_results.delete(request_id) if @sse_results.key?(request_id)
|
515
525
|
end
|
516
|
-
|
517
526
|
break if result || (Time.now - start_time > timeout)
|
518
527
|
|
519
528
|
sleep 0.1
|
520
529
|
end
|
521
|
-
|
522
530
|
return result if result
|
523
531
|
|
524
532
|
raise MCPClient::Errors::ToolCallError, "Timeout waiting for SSE result for request #{request_id}"
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
end
|
533
|
+
end
|
534
|
+
# Fallback: parse synchronous HTTP JSON response
|
535
|
+
begin
|
536
|
+
data = JSON.parse(response.body)
|
537
|
+
return data['result']
|
538
|
+
rescue JSON::ParserError => e
|
539
|
+
raise MCPClient::Errors::TransportError, "Invalid JSON response from server: #{e.message}"
|
533
540
|
end
|
534
541
|
end
|
535
542
|
ensure
|
data/lib/mcp_client/version.rb
CHANGED