ruby-mcp-client 0.3.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: 233c83227145cb76167e75784b7eaecef56828743f65f6256c84062bb2290b1e
4
- data.tar.gz: 4fb49b0cc82f5bd2b07d15ec200da4f9dced0fce52ddfacd4860a5a689152328
3
+ metadata.gz: 87cf7d5701adff89363dd653d263189bded230a32232bbbb496d41b4092afdec
4
+ data.tar.gz: 9a3561d2f97f0ef518cf75dfce82a8521140c9cdc381aaef3f3b3658265a7298
5
5
  SHA512:
6
- metadata.gz: bdd2b7223f25e4e8516f9bad648854a8e935b823d269762d769a545571960041041082f861a85e559a637e00bb9357541548bd87155bbe9df490c501fb755ade
7
- data.tar.gz: 3aef9c1fd10b846e46cc183c072443946a5aa39bfae43245949204e15e89a132df676af927cdabd4cae9c65e4052d220d4012bf0dadaffeb988cddf2a5ab7cfc
6
+ metadata.gz: 361f1916a531f14ded3292e15ea4947b0e3715d71d2be71dd24ea198616a82c55e516379a59a455b6c14f9fb7b3b9a1d02bdd457fde0b975ece6320bbb6da23b
7
+ data.tar.gz: b38270ec5a9ddce3a3689e6c8fb6e86efaa35453b6bcc8a590612d02fe1ef86040d62fd30fb95ff02640bb7b822bf8f473ae24fd571ad696069657f98a9d5c20
data/README.md CHANGED
@@ -55,6 +55,7 @@ client = MCPClient.create_client(
55
55
  read_timeout: 30, # Optional timeout in seconds (default: 30)
56
56
  retries: 3, # Optional number of retry attempts (default: 0)
57
57
  retry_backoff: 1 # Optional backoff delay in seconds (default: 1)
58
+ # Native support for tool streaming via call_tool_streaming method
58
59
  )
59
60
  ]
60
61
  )
@@ -75,7 +76,8 @@ results = client.call_tools([
75
76
  { name: 'tool2', parameters: { key2: 'value2' } }
76
77
  ])
77
78
 
78
- # Stream results (for supported transports like SSE)
79
+ # Stream results (supported by the SSE transport)
80
+ # Returns an Enumerator that yields results as they become available
79
81
  client.call_tool_streaming('streaming_tool', { param: 'value' }).each do |chunk|
80
82
  # Process each chunk as it arrives
81
83
  puts chunk
@@ -85,6 +87,23 @@ end
85
87
  openai_tools = client.to_openai_tools
86
88
  anthropic_tools = client.to_anthropic_tools
87
89
 
90
+ # Register for server notifications
91
+ client.on_notification do |server, method, params|
92
+ puts "Server notification: #{server.class} - #{method} - #{params}"
93
+ # Handle specific notifications based on method name
94
+ # 'notifications/tools/list_changed' is handled automatically by the client
95
+ end
96
+
97
+ # Send custom JSON-RPC requests or notifications
98
+ client.send_rpc('custom_method', params: { key: 'value' }, server: :sse) # Uses specific server
99
+ result = client.send_rpc('another_method', params: { data: 123 }) # Uses first available server
100
+ client.send_notification('status_update', params: { status: 'ready' })
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
+
88
107
  # Clear cached tools to force fresh fetch on next list
89
108
  client.clear_cache
90
109
  # Clean up connections
@@ -192,11 +211,18 @@ This client works with any MCP-compatible server, including:
192
211
 
193
212
  The SSE client implementation provides these key features:
194
213
 
195
- - **Robust connection handling**: Properly manages HTTP/HTTPS connections with configurable timeouts
214
+ - **Robust connection handling**: Properly manages HTTP/HTTPS connections with configurable timeouts and retries
196
215
  - **Thread safety**: All operations are thread-safe using monitors and synchronized access
197
216
  - **Reliable error handling**: Comprehensive error handling for network issues, timeouts, and malformed responses
198
- - **JSON-RPC over SSE**: Full implementation of JSON-RPC 2.0 over SSE transport
199
- - **Streaming support**: Native streaming for real-time updates
217
+ - **JSON-RPC over SSE**: Full implementation of JSON-RPC 2.0 over SSE transport with initialize handshake
218
+ - **Streaming support**: Native streaming for real-time updates via the `call_tool_streaming` method, which returns an Enumerator for processing results as they arrive
219
+ - **Notification support**: Built-in handling for JSON-RPC notifications with automatic tool cache invalidation and custom notification callback support
220
+ - **Custom RPC methods**: Send any custom JSON-RPC method or notification through `send_rpc` and `send_notification`
221
+ - **Configurable retries**: All RPC requests support configurable retries with exponential backoff
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
200
226
 
201
227
  ## Requirements
202
228
 
@@ -211,6 +237,16 @@ To implement a compatible MCP server you must:
211
237
  - Respond to `list_tools` requests with a JSON list of tools
212
238
  - Respond to `call_tool` requests by executing the specified tool
213
239
  - Return results (or errors) in JSON format
240
+ - Optionally send JSON-RPC notifications for events like tool updates
241
+
242
+ ### JSON-RPC Notifications
243
+
244
+ The client supports JSON-RPC notifications from the server:
245
+
246
+ - Default notification handler for `notifications/tools/list_changed` to automatically clear the tool cache
247
+ - Custom notification handling via the `on_notification` method
248
+ - Callbacks receive the server instance, method name, and parameters
249
+ - Multiple notification listeners can be registered
214
250
 
215
251
  ## Tool Schema
216
252
 
@@ -19,11 +19,24 @@ module MCPClient
19
19
  # @param logger [Logger, nil] optional logger, defaults to STDOUT
20
20
  def initialize(mcp_server_configs: [], logger: nil)
21
21
  @logger = logger || Logger.new($stdout, level: Logger::WARN)
22
+ @logger.progname = self.class.name
23
+ @logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
22
24
  @servers = mcp_server_configs.map do |config|
23
25
  @logger.debug("Creating server with config: #{config.inspect}")
24
26
  MCPClient::ServerFactory.create(config)
25
27
  end
26
28
  @tool_cache = {}
29
+ # JSON-RPC notification listeners
30
+ @notification_listeners = []
31
+ # Register default and user-defined notification handlers on each server
32
+ @servers.each do |server|
33
+ server.on_notification do |method, params|
34
+ # Default handling: clear tool cache on tools list change
35
+ clear_cache if method == 'notifications/tools/list_changed'
36
+ # Invoke user listeners
37
+ @notification_listeners.each { |cb| cb.call(server, method, params) }
38
+ end
39
+ end
27
40
  end
28
41
 
29
42
  # Lists all available tools from all connected MCP servers
@@ -92,6 +105,13 @@ module MCPClient
92
105
  @tool_cache.clear
93
106
  end
94
107
 
108
+ # Register a callback for JSON-RPC notifications from servers
109
+ # @yield [server, method, params]
110
+ # @return [void]
111
+ def on_notification(&block)
112
+ @notification_listeners << block
113
+ end
114
+
95
115
  # Find all tools whose name matches the given pattern (String or Regexp)
96
116
  # @param pattern [String, Regexp] pattern to match tool names
97
117
  # @return [Array<MCPClient::Tool>] matching tools
@@ -143,6 +163,28 @@ module MCPClient
143
163
  end
144
164
  end
145
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
+
146
188
  private
147
189
 
148
190
  # Validate parameters against tool JSON schema (checks required properties)
@@ -27,5 +27,36 @@ module MCPClient
27
27
  def cleanup
28
28
  raise NotImplementedError, 'Subclasses must implement cleanup'
29
29
  end
30
+
31
+ # Send a JSON-RPC request and return the result
32
+ # @param method [String] JSON-RPC method name
33
+ # @param params [Hash] parameters for the request
34
+ # @return [Object] result field from the JSON-RPC response
35
+ # @raise [MCPClient::Errors::ServerError, MCPClient::Errors::TransportError, MCPClient::Errors::ToolCallError]
36
+ def rpc_request(method, params = {})
37
+ raise NotImplementedError, 'Subclasses must implement rpc_request'
38
+ end
39
+
40
+ # Send a JSON-RPC notification (no response expected)
41
+ # @param method [String] JSON-RPC method name
42
+ # @param params [Hash] parameters for the notification
43
+ # @return [void]
44
+ def rpc_notify(method, params = {})
45
+ raise NotImplementedError, 'Subclasses must implement rpc_notify'
46
+ end
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
+
55
+ # Register a callback to receive JSON-RPC notifications
56
+ # @yield [method, params] invoked when a notification is received
57
+ # @return [void]
58
+ def on_notification(&block)
59
+ @notification_callback = block
60
+ end
30
61
  end
31
62
  end
@@ -9,7 +9,13 @@ module MCPClient
9
9
  def self.create(config)
10
10
  case config[:type]
11
11
  when 'stdio'
12
- MCPClient::ServerStdio.new(command: config[:command])
12
+ MCPClient::ServerStdio.new(
13
+ command: config[:command],
14
+ retries: config[:retries] || 0,
15
+ retry_backoff: config[:retry_backoff] || 1,
16
+ read_timeout: config[:read_timeout] || MCPClient::ServerStdio::READ_TIMEOUT,
17
+ logger: config[:logger]
18
+ )
13
19
  when 'sse'
14
20
  MCPClient::ServerSSE.new(
15
21
  base_url: config[:base_url],
@@ -11,7 +11,7 @@ module MCPClient
11
11
  # Implementation of MCP server that communicates via Server-Sent Events (SSE)
12
12
  # Useful for communicating with remote MCP servers over HTTP
13
13
  class ServerSSE < ServerBase
14
- attr_reader :base_url, :tools, :session_id, :http_client
14
+ attr_reader :base_url, :tools, :session_id, :http_client, :server_info, :capabilities
15
15
 
16
16
  # @param base_url [String] The base URL of the MCP server
17
17
  # @param headers [Hash] Additional headers to include in requests
@@ -22,9 +22,12 @@ module MCPClient
22
22
  def initialize(base_url:, headers: {}, read_timeout: 30, retries: 0, retry_backoff: 1, logger: nil)
23
23
  super()
24
24
  @logger = logger || Logger.new($stdout, level: Logger::WARN)
25
+ @logger.progname = self.class.name
26
+ @logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
25
27
  @max_retries = retries
26
28
  @retry_backoff = retry_backoff
27
- @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('/')
28
31
  @headers = headers.merge({
29
32
  'Accept' => 'text/event-stream',
30
33
  'Cache-Control' => 'no-cache',
@@ -42,6 +45,9 @@ module MCPClient
42
45
  @sse_connected = false
43
46
  @connection_established = false
44
47
  @connection_cv = @mutex.new_cond
48
+ @initialized = false
49
+ # Whether to use SSE transport; may disable if handshake fails
50
+ @use_sse = true
45
51
  end
46
52
 
47
53
  # Stream tool call fallback for SSE transport (yields single result)
@@ -64,7 +70,7 @@ module MCPClient
64
70
  return @tools if @tools
65
71
  end
66
72
 
67
- connect
73
+ ensure_initialized
68
74
 
69
75
  begin
70
76
  tools_data = request_tools_list
@@ -93,7 +99,7 @@ module MCPClient
93
99
  # @raise [MCPClient::Errors::TransportError] if response isn't valid JSON
94
100
  # @raise [MCPClient::Errors::ToolCallError] for other errors during tool execution
95
101
  def call_tool(tool_name, parameters)
96
- connect
102
+ ensure_initialized
97
103
 
98
104
  begin
99
105
  request_id = @mutex.synchronize { @request_id += 1 }
@@ -179,8 +185,85 @@ module MCPClient
179
185
  end
180
186
  end
181
187
 
188
+ # Generic JSON-RPC request: send method with params and return result
189
+ # @param method [String] JSON-RPC method name
190
+ # @param params [Hash] parameters for the request
191
+ # @return [Object] result from JSON-RPC response
192
+ def rpc_request(method, params = {})
193
+ ensure_initialized
194
+ with_retry do
195
+ request_id = @mutex.synchronize { @request_id += 1 }
196
+ request = { jsonrpc: '2.0', id: request_id, method: method, params: params }
197
+ send_jsonrpc_request(request)
198
+ end
199
+ end
200
+
201
+ # Send a JSON-RPC notification (no response expected)
202
+ # @param method [String] JSON-RPC method name
203
+ # @param params [Hash] parameters for the notification
204
+ # @return [void]
205
+ def rpc_notify(method, params = {})
206
+ ensure_initialized
207
+ url_base = @base_url.sub(%r{/sse/?$}, '')
208
+ uri = URI.parse("#{url_base}/messages?sessionId=#{@session_id}")
209
+ rpc_http = Net::HTTP.new(uri.host, uri.port)
210
+ if uri.scheme == 'https'
211
+ rpc_http.use_ssl = true
212
+ rpc_http.verify_mode = OpenSSL::SSL::VERIFY_PEER
213
+ end
214
+ rpc_http.open_timeout = 10
215
+ rpc_http.read_timeout = @read_timeout
216
+ rpc_http.keep_alive_timeout = 60
217
+ rpc_http.start do |http|
218
+ http_req = Net::HTTP::Post.new(uri)
219
+ http_req.content_type = 'application/json'
220
+ http_req.body = { jsonrpc: '2.0', method: method, params: params }.to_json
221
+ headers = @headers.dup
222
+ headers.except('Accept', 'Cache-Control').each { |k, v| http_req[k] = v }
223
+ response = http.request(http_req)
224
+ unless response.is_a?(Net::HTTPSuccess)
225
+ raise MCPClient::Errors::ServerError, "Notification failed: #{response.code} #{response.message}"
226
+ end
227
+ end
228
+ rescue StandardError => e
229
+ raise MCPClient::Errors::TransportError, "Failed to send notification: #{e.message}"
230
+ ensure
231
+ rpc_http.finish if rpc_http&.started?
232
+ end
233
+
182
234
  private
183
235
 
236
+ # Ensure SSE initialization handshake has been performed
237
+ def ensure_initialized
238
+ return if @initialized
239
+
240
+ connect
241
+ perform_initialize
242
+
243
+ @initialized = true
244
+ end
245
+
246
+ # Perform JSON-RPC initialize handshake with the MCP server
247
+ def perform_initialize
248
+ request_id = @mutex.synchronize { @request_id += 1 }
249
+ json_rpc_request = {
250
+ jsonrpc: '2.0',
251
+ id: request_id,
252
+ method: 'initialize',
253
+ params: {
254
+ 'protocolVersion' => MCPClient::VERSION,
255
+ 'capabilities' => {},
256
+ 'clientInfo' => { 'name' => 'ruby-mcp-client', 'version' => MCPClient::VERSION }
257
+ }
258
+ }
259
+ @logger.debug("Performing initialize RPC: #{json_rpc_request}")
260
+ result = send_jsonrpc_request(json_rpc_request)
261
+ return unless result.is_a?(Hash)
262
+
263
+ @server_info = result['serverInfo'] if result.key?('serverInfo')
264
+ @capabilities = result['capabilities'] if result.key?('capabilities')
265
+ end
266
+
184
267
  # Start the SSE thread to listen for events
185
268
  def start_sse_thread
186
269
  return if @sse_thread&.alive?
@@ -207,6 +290,7 @@ module MCPClient
207
290
  http.request(request) do |response|
208
291
  unless response.is_a?(Net::HTTPSuccess) && response['content-type']&.start_with?('text/event-stream')
209
292
  @mutex.synchronize do
293
+ # Signal connection attempt completed (failed)
210
294
  @connection_established = false
211
295
  @connection_cv.broadcast
212
296
  end
@@ -214,7 +298,10 @@ module MCPClient
214
298
  end
215
299
 
216
300
  @mutex.synchronize do
301
+ # Signal connection established and SSE ready
217
302
  @sse_connected = true
303
+ @connection_established = true
304
+ @connection_cv.broadcast
218
305
  end
219
306
 
220
307
  response.read_body do |chunk|
@@ -224,6 +311,11 @@ module MCPClient
224
311
  end
225
312
  end
226
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
227
319
  nil
228
320
  ensure
229
321
  sse_http&.finish if sse_http&.started?
@@ -272,6 +364,11 @@ module MCPClient
272
364
  when 'message'
273
365
  begin
274
366
  data = JSON.parse(event[:data])
367
+ # Dispatch JSON-RPC notifications (no id, has method)
368
+ if data['method'] && !data.key?('id')
369
+ @notification_callback&.call(data['method'], data['params'])
370
+ return
371
+ end
275
372
 
276
373
  @mutex.synchronize do
277
374
  @tools_data = data['result']['tools'] if data['result'] && data['result']['tools']
@@ -415,37 +512,31 @@ module MCPClient
415
512
  raise MCPClient::Errors::ServerError, "Server returned error: #{response.code} #{response.message}"
416
513
  end
417
514
 
418
- if response.code == '202'
515
+ # If SSE transport is enabled, retrieve the result via the SSE channel
516
+ if @use_sse
419
517
  request_id = request[:id]
420
-
421
518
  start_time = Time.now
422
- timeout = 10
519
+ timeout = @read_timeout || 10
423
520
  result = nil
424
521
 
425
522
  loop do
426
523
  @mutex.synchronize do
427
- if @sse_results[request_id]
428
- result = @sse_results[request_id]
429
- @sse_results.delete(request_id)
430
- end
524
+ result = @sse_results.delete(request_id) if @sse_results.key?(request_id)
431
525
  end
432
-
433
526
  break if result || (Time.now - start_time > timeout)
434
527
 
435
528
  sleep 0.1
436
529
  end
437
-
438
530
  return result if result
439
531
 
440
532
  raise MCPClient::Errors::ToolCallError, "Timeout waiting for SSE result for request #{request_id}"
441
-
442
- else
443
- begin
444
- data = JSON.parse(response.body)
445
- return data['result']
446
- rescue JSON::ParserError => e
447
- raise MCPClient::Errors::TransportError, "Invalid JSON response from server: #{e.message}"
448
- 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}"
449
540
  end
450
541
  end
451
542
  ensure
@@ -16,8 +16,9 @@ module MCPClient
16
16
  # @param command [String, Array] the stdio command to launch the MCP JSON-RPC server
17
17
  # @param retries [Integer] number of retry attempts on transient errors
18
18
  # @param retry_backoff [Numeric] base delay in seconds for exponential backoff
19
+ # @param read_timeout [Numeric] timeout in seconds for reading responses
19
20
  # @param logger [Logger, nil] optional logger
20
- def initialize(command:, retries: 0, retry_backoff: 1, logger: nil)
21
+ def initialize(command:, retries: 0, retry_backoff: 1, read_timeout: READ_TIMEOUT, logger: nil)
21
22
  super()
22
23
  @command = command.is_a?(Array) ? command.join(' ') : command
23
24
  @mutex = Mutex.new
@@ -26,8 +27,11 @@ module MCPClient
26
27
  @pending = {}
27
28
  @initialized = false
28
29
  @logger = logger || Logger.new($stdout, level: Logger::WARN)
30
+ @logger.progname = self.class.name
31
+ @logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
29
32
  @max_retries = retries
30
33
  @retry_backoff = retry_backoff
34
+ @read_timeout = read_timeout
31
35
  end
32
36
 
33
37
  # Connect to the MCP server by launching the command process via stdout/stdin
@@ -57,6 +61,12 @@ module MCPClient
57
61
  def handle_line(line)
58
62
  msg = JSON.parse(line)
59
63
  @logger.debug("Received line: #{line.chomp}")
64
+ # Dispatch JSON-RPC notifications (no id, has method)
65
+ if msg['method'] && !msg.key?('id')
66
+ @notification_callback&.call(msg['method'], msg['params'])
67
+ return
68
+ end
69
+ # Handle standard JSON-RPC responses
60
70
  id = msg['id']
61
71
  return unless id
62
72
 
@@ -188,7 +198,7 @@ module MCPClient
188
198
  end
189
199
 
190
200
  def wait_response(id)
191
- deadline = Time.now + READ_TIMEOUT
201
+ deadline = Time.now + @read_timeout
192
202
  @mutex.synchronize do
193
203
  until @pending.key?(id)
194
204
  remaining = deadline - Time.now
@@ -213,5 +223,45 @@ module MCPClient
213
223
  yielder << call_tool(tool_name, parameters)
214
224
  end
215
225
  end
226
+
227
+ # Generic JSON-RPC request: send method with params and wait for result
228
+ # @param method [String] JSON-RPC method
229
+ # @param params [Hash] parameters for the request
230
+ # @return [Object] result from JSON-RPC response
231
+ def rpc_request(method, params = {})
232
+ ensure_initialized
233
+ attempts = 0
234
+ begin
235
+ req_id = next_id
236
+ req = { 'jsonrpc' => '2.0', 'id' => req_id, 'method' => method, 'params' => params }
237
+ send_request(req)
238
+ res = wait_response(req_id)
239
+ if (err = res['error'])
240
+ raise MCPClient::Errors::ServerError, err['message']
241
+ end
242
+
243
+ res['result']
244
+ rescue MCPClient::Errors::ServerError, MCPClient::Errors::TransportError, IOError, Errno::ETIMEDOUT,
245
+ Errno::ECONNRESET => e
246
+ attempts += 1
247
+ if attempts <= @max_retries
248
+ delay = @retry_backoff * (2**(attempts - 1))
249
+ @logger.debug("Retry attempt #{attempts} after error: #{e.message}, sleeping #{delay}s")
250
+ sleep(delay)
251
+ retry
252
+ end
253
+ raise
254
+ end
255
+ end
256
+
257
+ # Send a JSON-RPC notification (no response expected)
258
+ # @param method [String] JSON-RPC method
259
+ # @param params [Hash] parameters for the notification
260
+ # @return [void]
261
+ def rpc_notify(method, params = {})
262
+ ensure_initialized
263
+ notif = { 'jsonrpc' => '2.0', 'method' => method, 'params' => params }
264
+ @stdin.puts(notif.to_json)
265
+ end
216
266
  end
217
267
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module MCPClient
4
4
  # Current version of the MCP client gem
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.1'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mcp-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Szymon Kurcab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-23 00:00:00.000000000 Z
11
+ date: 2025-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc