ruby-mcp-client 0.7.1 → 0.7.2
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 +48 -0
- data/lib/mcp_client/config_parser.rb +8 -8
- data/lib/mcp_client/http_transport_base.rb +1 -1
- data/lib/mcp_client/server_base.rb +18 -0
- data/lib/mcp_client/server_http.rb +2 -3
- data/lib/mcp_client/server_sse/json_rpc_transport.rb +8 -0
- data/lib/mcp_client/server_sse/reconnect_monitor.rb +1 -0
- data/lib/mcp_client/server_sse/sse_parser.rb +4 -4
- data/lib/mcp_client/server_sse.rb +65 -33
- data/lib/mcp_client/server_stdio.rb +1 -3
- data/lib/mcp_client/server_streamable_http.rb +2 -3
- data/lib/mcp_client/version.rb +3 -6
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e71d5420a77345079ef2da077240e6a578900c3acfd2c16c904b0a2bf84a8bb7
|
4
|
+
data.tar.gz: ea4f41ebef3d5898d3dd0786d50d08dbbd19057bbbd5b7aa8cc046d5a1608d7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a837ba7337913a453e5ae2567c67fed453b5e1cab3712705689c74b2efff27fa5c257750c834b5ac5bfff04333f08f1999b6813982d0c548ee92f4992c4d6aad
|
7
|
+
data.tar.gz: e10f2262b5dd10f0323dbbf870daa9f2d25d2d9f6a7f0c053870a8e0a5453ed9c5ed2c667211dfcf094c4fd56e7c12fbc1407c358e955fb14b4cbf4601312226
|
data/README.md
CHANGED
@@ -47,6 +47,8 @@ This Ruby MCP Client implements key features from the latest MCP specification (
|
|
47
47
|
### Implemented Features
|
48
48
|
- **OAuth 2.1 Authorization Framework** - Complete authentication with PKCE, dynamic client registration, server discovery, and runtime configuration
|
49
49
|
- **Streamable HTTP Transport** - Enhanced transport with Server-Sent Event formatted responses and session management
|
50
|
+
- **HTTP Redirect Support** - Automatic redirect handling for both SSE and HTTP transports with configurable limits
|
51
|
+
- **FastMCP Compatibility** - Full compatibility with FastMCP servers including proper line ending handling
|
50
52
|
|
51
53
|
## Usage
|
52
54
|
|
@@ -333,6 +335,48 @@ sse_client.cleanup
|
|
333
335
|
|
334
336
|
See `examples/mcp_sse_server_example.rb` for the full Playwright SSE example.
|
335
337
|
|
338
|
+
### FastMCP Example
|
339
|
+
|
340
|
+
The repository includes a complete FastMCP server example that demonstrates the Ruby MCP client working with a Python FastMCP server:
|
341
|
+
|
342
|
+
```ruby
|
343
|
+
# Start the FastMCP server
|
344
|
+
# python examples/echo_server.py
|
345
|
+
|
346
|
+
# Run the Ruby client
|
347
|
+
# bundle exec ruby examples/echo_server_client.rb
|
348
|
+
|
349
|
+
require 'mcp_client'
|
350
|
+
|
351
|
+
# Connect to FastMCP server
|
352
|
+
client = MCPClient.create_client(
|
353
|
+
mcp_server_configs: [
|
354
|
+
MCPClient.sse_config(
|
355
|
+
base_url: 'http://127.0.0.1:8000/sse/',
|
356
|
+
read_timeout: 30
|
357
|
+
)
|
358
|
+
]
|
359
|
+
)
|
360
|
+
|
361
|
+
# List available tools
|
362
|
+
tools = client.list_tools
|
363
|
+
puts "Found #{tools.length} tools:"
|
364
|
+
tools.each { |tool| puts "- #{tool.name}: #{tool.description}" }
|
365
|
+
|
366
|
+
# Use the tools
|
367
|
+
result = client.call_tool('echo', { message: 'Hello from Ruby!' })
|
368
|
+
result = client.call_tool('reverse', { text: 'FastMCP rocks!' })
|
369
|
+
|
370
|
+
client.cleanup
|
371
|
+
```
|
372
|
+
|
373
|
+
The FastMCP example includes:
|
374
|
+
- **`echo_server.py`** - A Python FastMCP server with 4 interactive tools
|
375
|
+
- **`echo_server_client.rb`** - Ruby client demonstrating all features
|
376
|
+
- **`README_ECHO_SERVER.md`** - Complete setup and usage instructions
|
377
|
+
|
378
|
+
This example showcases redirect support, proper line ending handling, and seamless integration between Ruby and Python MCP implementations.
|
379
|
+
|
336
380
|
### Integration Examples
|
337
381
|
|
338
382
|
The repository includes examples for integrating with popular AI APIs:
|
@@ -421,6 +465,7 @@ Complete examples can be found in the `examples/` directory:
|
|
421
465
|
- `ruby_anthropic_mcp.rb` - Integration with alexrudall/ruby-anthropic gem
|
422
466
|
- `gemini_ai_mcp.rb` - Integration with Google Vertex AI and Gemini models
|
423
467
|
- `mcp_sse_server_example.rb` - SSE transport with Playwright MCP
|
468
|
+
- `echo_server.py` & `echo_server_client.rb` - FastMCP server example with full setup
|
424
469
|
|
425
470
|
## MCP Server Compatibility
|
426
471
|
|
@@ -428,6 +473,7 @@ This client works with any MCP-compatible server, including:
|
|
428
473
|
|
429
474
|
- [@modelcontextprotocol/server-filesystem](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem) - File system access
|
430
475
|
- [@playwright/mcp](https://www.npmjs.com/package/@playwright/mcp) - Browser automation
|
476
|
+
- [FastMCP](https://github.com/jlowin/fastmcp) - Python framework for building MCP servers
|
431
477
|
- Custom servers implementing the MCP protocol
|
432
478
|
|
433
479
|
### Server Definition Files
|
@@ -645,6 +691,7 @@ For complete OAuth documentation, see [OAUTH.md](OAUTH.md).
|
|
645
691
|
The SSE client implementation provides these key features:
|
646
692
|
|
647
693
|
- **Robust connection handling**: Properly manages HTTP/HTTPS connections with configurable timeouts and retries
|
694
|
+
- **Automatic redirect support**: Follows HTTP redirects up to 3 hops for seamless server integration
|
648
695
|
- **Advanced connection management**:
|
649
696
|
- **Inactivity tracking**: Monitors connection activity to detect idle connections
|
650
697
|
- **Automatic ping**: Sends ping requests after a configurable period of inactivity (default: 10 seconds)
|
@@ -674,6 +721,7 @@ The SSE client implementation provides these key features:
|
|
674
721
|
The HTTP transport provides a simpler, stateless communication mechanism for MCP servers:
|
675
722
|
|
676
723
|
- **Request/Response Model**: Standard HTTP request/response cycle for each JSON-RPC call
|
724
|
+
- **Automatic redirect support**: Follows HTTP redirects up to 3 hops for seamless server integration
|
677
725
|
- **JSON-Only Responses**: Accepts only `application/json` responses (no SSE support)
|
678
726
|
- **Session Support**: Automatic session header (`Mcp-Session-Id`) capture and injection for session-based MCP servers
|
679
727
|
- **Session Termination**: Proper session cleanup with HTTP DELETE requests during connection teardown
|
@@ -27,7 +27,7 @@ module MCPClient
|
|
27
27
|
|
28
28
|
result = {}
|
29
29
|
servers_data.each do |server_name, config|
|
30
|
-
next unless
|
30
|
+
next unless valid_server_config?(config, server_name)
|
31
31
|
|
32
32
|
server_config = process_server_config(config, server_name)
|
33
33
|
next unless server_config
|
@@ -66,7 +66,7 @@ module MCPClient
|
|
66
66
|
# @param config [Object] server configuration to validate
|
67
67
|
# @param server_name [String] name of the server
|
68
68
|
# @return [Boolean] true if valid, false otherwise
|
69
|
-
def
|
69
|
+
def valid_server_config?(config, server_name)
|
70
70
|
return true if config.is_a?(Hash)
|
71
71
|
|
72
72
|
@logger.warn("Configuration for server '#{server_name}' is not an object; skipping.")
|
@@ -86,11 +86,11 @@ module MCPClient
|
|
86
86
|
when 'stdio'
|
87
87
|
parse_stdio_config(clean, config, server_name)
|
88
88
|
when 'sse'
|
89
|
-
return nil unless parse_sse_config(clean, config, server_name)
|
89
|
+
return nil unless parse_sse_config?(clean, config, server_name)
|
90
90
|
when 'streamable_http'
|
91
|
-
return nil unless parse_streamable_http_config(clean, config, server_name)
|
91
|
+
return nil unless parse_streamable_http_config?(clean, config, server_name)
|
92
92
|
when 'http'
|
93
|
-
return nil unless parse_http_config(clean, config, server_name)
|
93
|
+
return nil unless parse_http_config?(clean, config, server_name)
|
94
94
|
else
|
95
95
|
@logger.warn("Unrecognized type '#{type}' for server '#{server_name}'; skipping.")
|
96
96
|
return nil
|
@@ -164,7 +164,7 @@ module MCPClient
|
|
164
164
|
# @param config [Hash] raw configuration from JSON
|
165
165
|
# @param server_name [String] name of the server for error reporting
|
166
166
|
# @return [Boolean] true if parsing succeeded, false if required elements are missing
|
167
|
-
def parse_sse_config(clean, config, server_name)
|
167
|
+
def parse_sse_config?(clean, config, server_name)
|
168
168
|
# URL is required
|
169
169
|
source = config['url']
|
170
170
|
unless source
|
@@ -192,7 +192,7 @@ module MCPClient
|
|
192
192
|
# @param config [Hash] raw configuration from JSON
|
193
193
|
# @param server_name [String] name of the server for error reporting
|
194
194
|
# @return [Boolean] true if parsing succeeded, false if required elements are missing
|
195
|
-
def parse_streamable_http_config(clean, config, server_name)
|
195
|
+
def parse_streamable_http_config?(clean, config, server_name)
|
196
196
|
# URL is required
|
197
197
|
source = config['url']
|
198
198
|
unless source
|
@@ -225,7 +225,7 @@ module MCPClient
|
|
225
225
|
# @param config [Hash] raw configuration from JSON
|
226
226
|
# @param server_name [String] name of the server for error reporting
|
227
227
|
# @return [Boolean] true if parsing succeeded, false if required elements are missing
|
228
|
-
def parse_http_config(clean, config, server_name)
|
228
|
+
def parse_http_config?(clean, config, server_name)
|
229
229
|
# URL is required
|
230
230
|
source = config['url']
|
231
231
|
unless source
|
@@ -119,7 +119,7 @@ module MCPClient
|
|
119
119
|
# @return [Hash] the initialization parameters
|
120
120
|
def initialization_params
|
121
121
|
{
|
122
|
-
'protocolVersion' => MCPClient::
|
122
|
+
'protocolVersion' => MCPClient::PROTOCOL_VERSION,
|
123
123
|
'capabilities' => {},
|
124
124
|
'clientInfo' => { 'name' => 'ruby-mcp-client', 'version' => MCPClient::VERSION }
|
125
125
|
}
|
@@ -77,5 +77,23 @@ module MCPClient
|
|
77
77
|
def on_notification(&block)
|
78
78
|
@notification_callback = block
|
79
79
|
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
# Initialize logger with proper formatter handling
|
84
|
+
# Preserves custom formatter if logger is provided, otherwise sets a default formatter
|
85
|
+
# @param logger [Logger, nil] custom logger to use, or nil to create a default one
|
86
|
+
# @return [Logger] the configured logger
|
87
|
+
def initialize_logger(logger)
|
88
|
+
if logger
|
89
|
+
@logger = logger
|
90
|
+
@logger.progname = self.class.name
|
91
|
+
else
|
92
|
+
@logger = Logger.new($stdout, level: Logger::WARN)
|
93
|
+
@logger.progname = self.class.name
|
94
|
+
@logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
|
95
|
+
end
|
96
|
+
@logger
|
97
|
+
end
|
80
98
|
end
|
81
99
|
end
|
@@ -6,6 +6,7 @@ require 'monitor'
|
|
6
6
|
require 'logger'
|
7
7
|
require 'faraday'
|
8
8
|
require 'faraday/retry'
|
9
|
+
require 'faraday/follow_redirects'
|
9
10
|
|
10
11
|
module MCPClient
|
11
12
|
# Implementation of MCP server that communicates via HTTP requests/responses
|
@@ -49,9 +50,7 @@ module MCPClient
|
|
49
50
|
def initialize(base_url:, **options)
|
50
51
|
opts = default_options.merge(options)
|
51
52
|
super(name: opts[:name])
|
52
|
-
|
53
|
-
@logger.progname = self.class.name
|
54
|
-
@logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
|
53
|
+
initialize_logger(opts[:logger])
|
55
54
|
|
56
55
|
@max_retries = opts[:retries]
|
57
56
|
@retry_backoff = opts[:retry_backoff]
|
@@ -70,6 +70,13 @@ module MCPClient
|
|
70
70
|
|
71
71
|
@server_info = result['serverInfo']
|
72
72
|
@capabilities = result['capabilities']
|
73
|
+
|
74
|
+
# Send initialized notification to acknowledge completion of initialization
|
75
|
+
initialized_notification = build_jsonrpc_notification('notifications/initialized', {})
|
76
|
+
post_json_rpc_request(initialized_notification)
|
77
|
+
|
78
|
+
# Small delay to ensure server processes the notification
|
79
|
+
sleep(0.1)
|
73
80
|
end
|
74
81
|
|
75
82
|
# Send a JSON-RPC request to the server and wait for result
|
@@ -133,6 +140,7 @@ module MCPClient
|
|
133
140
|
def create_json_rpc_connection(base_url)
|
134
141
|
Faraday.new(url: base_url) do |f|
|
135
142
|
f.request :retry, max: @max_retries, interval: @retry_backoff, backoff_factor: 2
|
143
|
+
f.response :follow_redirects, limit: 3
|
136
144
|
f.options.open_timeout = @read_timeout
|
137
145
|
f.options.timeout = @read_timeout
|
138
146
|
f.adapter Faraday.default_adapter
|
@@ -191,6 +191,7 @@ module MCPClient
|
|
191
191
|
f.options.open_timeout = 10
|
192
192
|
f.options.timeout = nil
|
193
193
|
f.request :retry, max: @max_retries, interval: @retry_backoff, backoff_factor: 2
|
194
|
+
f.response :follow_redirects, limit: 3
|
194
195
|
f.adapter Faraday.default_adapter
|
195
196
|
end
|
196
197
|
|
@@ -31,9 +31,9 @@ module MCPClient
|
|
31
31
|
data = JSON.parse(event[:data])
|
32
32
|
|
33
33
|
return if process_error_in_message(data)
|
34
|
-
return if process_notification(data)
|
34
|
+
return if process_notification?(data)
|
35
35
|
|
36
|
-
process_response(data)
|
36
|
+
process_response?(data)
|
37
37
|
rescue MCPClient::Errors::ConnectionError
|
38
38
|
raise
|
39
39
|
rescue JSON::ParserError => e
|
@@ -61,7 +61,7 @@ module MCPClient
|
|
61
61
|
# Process a JSON-RPC notification (no id => notification)
|
62
62
|
# @param data [Hash] the parsed JSON payload
|
63
63
|
# @return [Boolean] true if we saw & handled a notification
|
64
|
-
def process_notification(data)
|
64
|
+
def process_notification?(data)
|
65
65
|
return false unless data['method'] && !data.key?('id')
|
66
66
|
|
67
67
|
@notification_callback&.call(data['method'], data['params'])
|
@@ -71,7 +71,7 @@ module MCPClient
|
|
71
71
|
# Process a JSON-RPC response (id => response)
|
72
72
|
# @param data [Hash] the parsed JSON payload
|
73
73
|
# @return [Boolean] true if we saw & handled a response
|
74
|
-
def process_response(data)
|
74
|
+
def process_response?(data)
|
75
75
|
return false unless data['id']
|
76
76
|
|
77
77
|
@mutex.synchronize do
|
@@ -6,6 +6,7 @@ require 'monitor'
|
|
6
6
|
require 'logger'
|
7
7
|
require 'faraday'
|
8
8
|
require 'faraday/retry'
|
9
|
+
require 'faraday/follow_redirects'
|
9
10
|
|
10
11
|
module MCPClient
|
11
12
|
# Implementation of MCP server that communicates via Server-Sent Events (SSE)
|
@@ -56,13 +57,11 @@ module MCPClient
|
|
56
57
|
def initialize(base_url:, headers: {}, read_timeout: 30, ping: 10,
|
57
58
|
retries: 0, retry_backoff: 1, name: nil, logger: nil)
|
58
59
|
super(name: name)
|
59
|
-
|
60
|
-
@logger.progname = self.class.name
|
61
|
-
@logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
|
60
|
+
initialize_logger(logger)
|
62
61
|
@max_retries = retries
|
63
62
|
@retry_backoff = retry_backoff
|
64
|
-
# Normalize base_url:
|
65
|
-
@base_url = base_url
|
63
|
+
# Normalize base_url: preserve trailing slash if explicitly provided for SSE endpoints
|
64
|
+
@base_url = base_url
|
66
65
|
@headers = headers.merge({
|
67
66
|
'Accept' => 'text/event-stream',
|
68
67
|
'Cache-Control' => 'no-cache',
|
@@ -369,53 +368,86 @@ module MCPClient
|
|
369
368
|
record_activity if chunk.include?('event:')
|
370
369
|
|
371
370
|
# Check for direct JSON error responses (which aren't proper SSE events)
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
371
|
+
handle_json_error_response(chunk)
|
372
|
+
|
373
|
+
event_buffers = extract_complete_events(chunk)
|
374
|
+
|
375
|
+
# Process extracted events outside the mutex to avoid deadlocks
|
376
|
+
event_buffers&.each { |event_data| parse_and_handle_sse_event(event_data) }
|
377
|
+
end
|
378
|
+
|
379
|
+
# Check if the error represents an authorization error
|
380
|
+
# @param error_message [String] The error message from the server
|
381
|
+
# @param error_code [Integer, nil] The error code if available
|
382
|
+
# @return [Boolean] True if it's an authorization error
|
383
|
+
# @private
|
384
|
+
def authorization_error?(error_message, error_code)
|
385
|
+
return true if error_message.include?('Unauthorized') || error_message.include?('authentication')
|
386
|
+
return true if [401, -32_000].include?(error_code)
|
378
387
|
|
379
|
-
|
380
|
-
|
388
|
+
false
|
389
|
+
end
|
381
390
|
|
382
|
-
|
383
|
-
|
384
|
-
|
391
|
+
# Handle JSON error responses embedded in SSE chunks
|
392
|
+
# @param chunk [String] the chunk to check for JSON errors
|
393
|
+
# @return [void]
|
394
|
+
# @raise [MCPClient::Errors::ConnectionError] if authentication error is found
|
395
|
+
# @private
|
396
|
+
def handle_json_error_response(chunk)
|
397
|
+
return unless chunk.start_with?('{') && chunk.include?('"error"') &&
|
398
|
+
(chunk.include?('Unauthorized') || chunk.include?('authentication'))
|
385
399
|
|
386
|
-
|
400
|
+
begin
|
401
|
+
data = JSON.parse(chunk)
|
402
|
+
if data['error']
|
403
|
+
error_message = data['error']['message'] || 'Unknown server error'
|
404
|
+
|
405
|
+
@mutex.synchronize do
|
406
|
+
@auth_error = "Authorization failed: #{error_message}"
|
407
|
+
@connection_established = false
|
408
|
+
@connection_cv.broadcast
|
387
409
|
end
|
388
|
-
|
389
|
-
|
410
|
+
|
411
|
+
raise MCPClient::Errors::ConnectionError, "Authorization failed: #{error_message}"
|
390
412
|
end
|
413
|
+
rescue JSON::ParserError
|
414
|
+
# Not valid JSON, process normally
|
391
415
|
end
|
416
|
+
end
|
392
417
|
|
418
|
+
# Extract complete SSE events from the buffer
|
419
|
+
# @param chunk [String] the chunk to add to the buffer
|
420
|
+
# @return [Array<String>, nil] array of complete events or nil if none
|
421
|
+
# @private
|
422
|
+
def extract_complete_events(chunk)
|
393
423
|
event_buffers = nil
|
394
424
|
@mutex.synchronize do
|
395
425
|
@buffer += chunk
|
396
426
|
|
397
427
|
# Extract all complete events from the buffer
|
428
|
+
# Handle both Unix (\n\n) and Windows (\r\n\r\n) line endings
|
398
429
|
event_buffers = []
|
399
|
-
while (event_end = @buffer.index("\n\n"))
|
400
|
-
event_data =
|
430
|
+
while (event_end = @buffer.index("\n\n") || @buffer.index("\r\n\r\n"))
|
431
|
+
event_data = extract_single_event(event_end)
|
401
432
|
event_buffers << event_data
|
402
433
|
end
|
403
434
|
end
|
404
|
-
|
405
|
-
# Process extracted events outside the mutex to avoid deadlocks
|
406
|
-
event_buffers&.each { |event_data| parse_and_handle_sse_event(event_data) }
|
435
|
+
event_buffers
|
407
436
|
end
|
408
437
|
|
409
|
-
#
|
410
|
-
# @param
|
411
|
-
# @
|
412
|
-
# @return [Boolean] True if it's an authorization error
|
438
|
+
# Extract a single event from the buffer
|
439
|
+
# @param event_end [Integer] the position where the event ends
|
440
|
+
# @return [String] the extracted event data
|
413
441
|
# @private
|
414
|
-
def
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
442
|
+
def extract_single_event(event_end)
|
443
|
+
# Determine the line ending style and extract accordingly
|
444
|
+
crlf_index = @buffer.index("\r\n\r\n")
|
445
|
+
lf_index = @buffer.index("\n\n")
|
446
|
+
if crlf_index && (lf_index.nil? || crlf_index < lf_index)
|
447
|
+
@buffer.slice!(0, event_end + 4) # \r\n\r\n is 4 chars
|
448
|
+
else
|
449
|
+
@buffer.slice!(0, event_end + 2) # \n\n is 2 chars
|
450
|
+
end
|
419
451
|
end
|
420
452
|
|
421
453
|
# Handle authorization error in SSE message
|
@@ -39,9 +39,7 @@ module MCPClient
|
|
39
39
|
@next_id = 1
|
40
40
|
@pending = {}
|
41
41
|
@initialized = false
|
42
|
-
|
43
|
-
@logger.progname = self.class.name
|
44
|
-
@logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
|
42
|
+
initialize_logger(logger)
|
45
43
|
@max_retries = retries
|
46
44
|
@retry_backoff = retry_backoff
|
47
45
|
@read_timeout = read_timeout
|
@@ -6,6 +6,7 @@ require 'monitor'
|
|
6
6
|
require 'logger'
|
7
7
|
require 'faraday'
|
8
8
|
require 'faraday/retry'
|
9
|
+
require 'faraday/follow_redirects'
|
9
10
|
|
10
11
|
module MCPClient
|
11
12
|
# Implementation of MCP server that communicates via Streamable HTTP transport
|
@@ -41,9 +42,7 @@ module MCPClient
|
|
41
42
|
def initialize(base_url:, **options)
|
42
43
|
opts = default_options.merge(options)
|
43
44
|
super(name: opts[:name])
|
44
|
-
|
45
|
-
@logger.progname = self.class.name
|
46
|
-
@logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
|
45
|
+
initialize_logger(opts[:logger])
|
47
46
|
|
48
47
|
@max_retries = opts[:retries]
|
49
48
|
@retry_backoff = opts[:retry_backoff]
|
data/lib/mcp_client/version.rb
CHANGED
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
module MCPClient
|
4
4
|
# Current version of the MCP client gem
|
5
|
-
VERSION = '0.7.
|
5
|
+
VERSION = '0.7.2'
|
6
6
|
|
7
|
-
#
|
8
|
-
PROTOCOL_VERSION = '
|
9
|
-
|
10
|
-
# Protocol version for HTTP and Streamable HTTP transports
|
11
|
-
HTTP_PROTOCOL_VERSION = '2025-03-26'
|
7
|
+
# MCP protocol version (date-based) - unified across all transports
|
8
|
+
PROTOCOL_VERSION = '2025-03-26'
|
12
9
|
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.7.
|
4
|
+
version: 0.7.2
|
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-
|
11
|
+
date: 2025-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday-follow_redirects
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: faraday-retry
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|