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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe40b5481b8c0b59201585bfe54fb912c71235d4f02ada0c8a32a908155c319d
4
- data.tar.gz: a378e9f8bd616e92739d371dfb458fc9385a6f2bbc62287cdf1e705742cfa6e0
3
+ metadata.gz: 87cf7d5701adff89363dd653d263189bded230a32232bbbb496d41b4092afdec
4
+ data.tar.gz: 9a3561d2f97f0ef518cf75dfce82a8521140c9cdc381aaef3f3b3658265a7298
5
5
  SHA512:
6
- metadata.gz: 19cb60e6ebf6693a3ee312e592b94e891b568bb67fc675b732d3c32c6452260486fe3f7197a4626e933096cbb52ae4afae8aaed763906fba3041adcadb7ee8ce
7
- data.tar.gz: c4c9d8a8b7aaf40d277bd20cb7aa50caf1ade083d6569e9825bb6dc53378d1359ef40e7dab84397416df950887b705abca7738179f639bd965ecb23e3d8186ea
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
 
@@ -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
- @base_url = base_url.end_with?('/') ? base_url : "#{base_url}/"
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 handshake initialization has been performed
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
- if response.code == '202'
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[request_id]
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
- else
527
- begin
528
- data = JSON.parse(response.body)
529
- return data['result']
530
- rescue JSON::ParserError => e
531
- raise MCPClient::Errors::TransportError, "Invalid JSON response from server: #{e.message}"
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module MCPClient
4
4
  # Current version of the MCP client gem
5
- VERSION = '0.4.0'
5
+ VERSION = '0.4.1'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mcp-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Szymon Kurcab