ruby-mcp-client 0.5.3 → 0.6.0

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: 2ec468ae4d2033287bfc89a788e87acc878ab833552e98bef88c95176fa6a906
4
- data.tar.gz: d6f2f676be6932d8dbe641df40674aa77ef64d1296d9fdf4eb7c892537f2fa0c
3
+ metadata.gz: a6607aff3aa197734ac1b5fd4c2f51bb994df18ffcb0f18928c4f75197aaa8ed
4
+ data.tar.gz: 6921d74355c4497ca944374795469a767873ff8716e54fedbe3438104a4e5b79
5
5
  SHA512:
6
- metadata.gz: 49f5427d6ffc9bc5632bed9b56e4c03e70ce4894d087331f12fd340b60ce82a539b9b2a362c1ce8d0b9686d9d9a4dae4bea3d11ec732b8a0ae34a3e898ff43b2
7
- data.tar.gz: 3e32823ef422661fbcf507caf141d0ce65aedd0661ecb54f3b2a287565b630049d2acdfc4bb7b9a93a965e75403b635e43eb21259f035d8b82810f58e0fff304
6
+ metadata.gz: cea51f3ff6046ac619197413ccbe50a18fc3aeef6f881180d9cc5b0ad49b67d1cbe93459005f93f3063ad2dfe699c06bb1556180dc3bd1437d67e38b89549c9a
7
+ data.tar.gz: 8de670f6d7f3fd5a3c9786365e19034ee6d8734ef8d938769818acb606c8f7c13529b8777cdff2a54483398d52dd7847ccddc957a8a4517c16ad31a1b35700b0
data/README.md CHANGED
@@ -48,23 +48,31 @@ require 'mcp_client'
48
48
  client = MCPClient.create_client(
49
49
  mcp_server_configs: [
50
50
  # Local stdio server
51
- MCPClient.stdio_config(command: 'npx -y @modelcontextprotocol/server-filesystem /home/user'),
51
+ MCPClient.stdio_config(
52
+ command: 'npx -y @modelcontextprotocol/server-filesystem /home/user',
53
+ name: 'filesystem' # Optional name for this server
54
+ ),
52
55
  # Remote HTTP SSE server (with streaming support)
53
56
  MCPClient.sse_config(
54
57
  base_url: 'https://api.example.com/sse',
55
58
  headers: { 'Authorization' => 'Bearer YOUR_TOKEN' },
59
+ name: 'api', # Optional name for this server
56
60
  read_timeout: 30, # Optional timeout in seconds (default: 30)
57
61
  ping: 10, # Optional ping interval in seconds of inactivity (default: 10)
58
62
  # Connection closes automatically after inactivity (2.5x ping interval)
59
63
  retries: 3, # Optional number of retry attempts (default: 0)
60
- retry_backoff: 1 # Optional backoff delay in seconds (default: 1)
64
+ retry_backoff: 1, # Optional backoff delay in seconds (default: 1)
61
65
  # Native support for tool streaming via call_tool_streaming method
62
- ) ]
66
+ logger: Logger.new($stdout, level: Logger::INFO) # Optional logger for this server
67
+ ) ],
68
+ # Optional logger for the client and all servers without explicit loggers
69
+ logger: Logger.new($stdout, level: Logger::WARN)
63
70
  )
64
71
 
65
72
  # Or load server definitions from a JSON file
66
73
  client = MCPClient.create_client(
67
- server_definition_file: 'path/to/server_definition.json'
74
+ server_definition_file: 'path/to/server_definition.json',
75
+ logger: Logger.new($stdout, level: Logger::WARN) # Optional logger for client and servers
68
76
  )
69
77
 
70
78
  # MCP server configuration JSON format can be:
@@ -74,10 +82,14 @@ client = MCPClient.create_client(
74
82
  # [{ "type": "stdio", "command": "npx server" }, { "type": "sse", "url": "http://..." }]
75
83
  # 3. An object with "mcpServers" key containing named servers:
76
84
  # { "mcpServers": { "server1": { "type": "sse", "url": "http://..." } } }
85
+ # Note: When using this format, server1 will be accessible by name
77
86
 
78
87
  # List available tools
79
88
  tools = client.list_tools
80
89
 
90
+ # Find a server by name
91
+ filesystem_server = client.find_server('filesystem')
92
+
81
93
  # Find tools by name pattern (string or regex)
82
94
  file_tools = client.find_tools('file')
83
95
  first_tool = client.find_tool(/^file_/)
@@ -85,15 +97,20 @@ first_tool = client.find_tool(/^file_/)
85
97
  # Call a specific tool by name
86
98
  result = client.call_tool('example_tool', { param1: 'value1', param2: 42 })
87
99
 
100
+ # Call a tool on a specific server by name
101
+ result = client.call_tool('example_tool', { param1: 'value1' }, server: 'filesystem')
102
+ # You can also call a tool on a server directly
103
+ result = filesystem_server.call_tool('example_tool', { param1: 'value1' })
104
+
88
105
  # Call multiple tools in batch
89
106
  results = client.call_tools([
90
107
  { name: 'tool1', parameters: { key1: 'value1' } },
91
- { name: 'tool2', parameters: { key2: 'value2' } }
108
+ { name: 'tool2', parameters: { key2: 'value2' }, server: 'filesystem' } # Specify server for a specific tool
92
109
  ])
93
110
 
94
111
  # Stream results (supported by the SSE transport)
95
112
  # Returns an Enumerator that yields results as they become available
96
- client.call_tool_streaming('streaming_tool', { param: 'value' }).each do |chunk|
113
+ client.call_tool_streaming('streaming_tool', { param: 'value' }, server: 'api').each do |chunk|
97
114
  # Process each chunk as it arrives
98
115
  puts chunk
99
116
  end
@@ -105,13 +122,14 @@ google_tools = client.to_google_tools
105
122
 
106
123
  # Register for server notifications
107
124
  client.on_notification do |server, method, params|
108
- puts "Server notification: #{server.class} - #{method} - #{params}"
125
+ puts "Server notification: #{server.class}[#{server.name}] - #{method} - #{params}"
109
126
  # Handle specific notifications based on method name
110
127
  # 'notifications/tools/list_changed' is handled automatically by the client
111
128
  end
112
129
 
113
130
  # Send custom JSON-RPC requests or notifications
114
- client.send_rpc('custom_method', params: { key: 'value' }, server: :sse) # Uses specific server
131
+ client.send_rpc('custom_method', params: { key: 'value' }, server: :sse) # Uses specific server by type
132
+ client.send_rpc('custom_method', params: { key: 'value' }, server: 'filesystem') # Uses specific server by name
115
133
  result = client.send_rpc('another_method', params: { data: 123 }) # Uses first available server
116
134
  client.send_notification('status_update', params: { status: 'ready' })
117
135
 
@@ -146,7 +164,8 @@ sse_client = MCPClient.create_client(
146
164
  read_timeout: 30, # Timeout in seconds for request fulfillment
147
165
  ping: 10, # Send ping after 10 seconds of inactivity
148
166
  # Connection closes automatically after inactivity (2.5x ping interval)
149
- retries: 2 # Number of retry attempts on transient errors
167
+ retries: 2, # Number of retry attempts on transient errors
168
+ logger: logger # Optional logger for debugging connection issues
150
169
  )
151
170
  ]
152
171
  )
@@ -342,15 +361,19 @@ Special configuration options:
342
361
 
343
362
  - **Multiple transports** - Support for both stdio and SSE transports
344
363
  - **Multiple servers** - Connect to multiple MCP servers simultaneously
364
+ - **Named servers** - Associate names with servers and find/reference them by name
365
+ - **Server lookup** - Find servers by name using `find_server`
366
+ - **Tool association** - Each tool knows which server it belongs to
345
367
  - **Tool discovery** - Find tools by name or pattern
368
+ - **Server disambiguation** - Specify which server to use when tools with same name exist in multiple servers
346
369
  - **Atomic tool calls** - Simple API for invoking tools with parameters
347
370
  - **Batch support** - Call multiple tools in a single operation
348
- - **API conversions** - Built-in format conversion for OpenAI and Anthropic APIs
371
+ - **API conversions** - Built-in format conversion for OpenAI, Anthropic, and Google Vertex AI APIs
349
372
  - **Thread safety** - Synchronized access for thread-safe operation
350
373
  - **Server notifications** - Support for JSON-RPC notifications
351
374
  - **Custom RPC methods** - Send any custom JSON-RPC method
352
375
  - **Consistent error handling** - Rich error types for better exception handling
353
- - **JSON configuration** - Support for server definition files in JSON format
376
+ - **JSON configuration** - Support for server definition files in JSON format with name retention
354
377
 
355
378
  ### Server-Sent Events (SSE) Implementation
356
379
 
@@ -362,6 +385,13 @@ The SSE client implementation provides these key features:
362
385
  - **Automatic ping**: Sends ping requests after a configurable period of inactivity (default: 10 seconds)
363
386
  - **Automatic disconnection**: Closes idle connections after inactivity (2.5× ping interval)
364
387
  - **MCP compliant**: Any server communication resets the inactivity timer per specification
388
+ - **Intelligent reconnection**:
389
+ - **Ping failure detection**: Tracks consecutive ping failures (when server isn't responding)
390
+ - **Automatic reconnection**: Attempts to reconnect after 3 consecutive ping failures
391
+ - **Exponential backoff**: Uses increasing delays between reconnection attempts
392
+ - **Smart retry limits**: Caps reconnection attempts (default: 5) to avoid infinite loops
393
+ - **Connection state monitoring**: Properly detects and handles closed connections to prevent errors
394
+ - **Failure transparency**: Handles reconnection in the background without disrupting client code
365
395
  - **Thread safety**: All operations are thread-safe using monitors and synchronized access
366
396
  - **Reliable error handling**: Comprehensive error handling for network issues, timeouts, and malformed responses
367
397
  - **JSON-RPC over SSE**: Full implementation of JSON-RPC 2.0 over SSE transport with initialize handshake
@@ -23,7 +23,7 @@ module MCPClient
23
23
  @logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" }
24
24
  @servers = mcp_server_configs.map do |config|
25
25
  @logger.debug("Creating server with config: #{config.inspect}")
26
- MCPClient::ServerFactory.create(config)
26
+ MCPClient::ServerFactory.create(config, logger: @logger)
27
27
  end
28
28
  @tool_cache = {}
29
29
  # JSON-RPC notification listeners
@@ -42,15 +42,35 @@ module MCPClient
42
42
  # Lists all available tools from all connected MCP servers
43
43
  # @param cache [Boolean] whether to use cached tools or fetch fresh
44
44
  # @return [Array<MCPClient::Tool>] list of available tools
45
+ # @raise [MCPClient::Errors::ConnectionError] on authorization failures
46
+ # @raise [MCPClient::Errors::ToolCallError] if no tools could be retrieved from any server
45
47
  def list_tools(cache: true)
46
48
  return @tool_cache.values if cache && !@tool_cache.empty?
47
49
 
48
50
  tools = []
51
+ connection_errors = []
52
+
49
53
  servers.each do |server|
50
54
  server.list_tools.each do |tool|
51
55
  @tool_cache[tool.name] = tool
52
56
  tools << tool
53
57
  end
58
+ rescue MCPClient::Errors::ConnectionError => e
59
+ # Fast-fail on authorization errors for better user experience
60
+ # If this is the first server or we haven't collected any tools yet,
61
+ # raise the auth error directly to avoid cascading error messages
62
+ raise e if e.message.include?('Authorization failed') && tools.empty?
63
+
64
+ # Store the error and try other servers
65
+ connection_errors << e
66
+ @logger.error("Server error: #{e.message}")
67
+ end
68
+
69
+ # If we didn't get any tools from any server but have servers configured, report failure
70
+ if tools.empty? && !servers.empty?
71
+ raise connection_errors.first if connection_errors.any?
72
+
73
+ raise MCPClient::Errors::ToolCallError, 'Failed to retrieve tools from any server'
54
74
  end
55
75
 
56
76
  tools
@@ -59,21 +79,51 @@ module MCPClient
59
79
  # Calls a specific tool by name with the given parameters
60
80
  # @param tool_name [String] the name of the tool to call
61
81
  # @param parameters [Hash] the parameters to pass to the tool
82
+ # @param server [String, Symbol, Integer, MCPClient::ServerBase, nil] optional server to use
62
83
  # @return [Object] the result of the tool invocation
63
- def call_tool(tool_name, parameters)
84
+ def call_tool(tool_name, parameters, server: nil)
64
85
  tools = list_tools
65
- tool = tools.find { |t| t.name == tool_name }
66
86
 
67
- raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found" unless tool
87
+ if server
88
+ # Use the specified server
89
+ srv = select_server(server)
90
+ # Find the tool on this specific server
91
+ tool = tools.find { |t| t.name == tool_name && t.server == srv }
92
+ unless tool
93
+ raise MCPClient::Errors::ToolNotFound,
94
+ "Tool '#{tool_name}' not found on server '#{srv.name || srv.class.name}'"
95
+ end
96
+ else
97
+ # Find the tool across all servers
98
+ matching_tools = tools.select { |t| t.name == tool_name }
99
+
100
+ if matching_tools.empty?
101
+ raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found"
102
+ elsif matching_tools.size > 1
103
+ # If multiple matches, disambiguate with server names
104
+ server_names = matching_tools.map { |t| t.server&.name || 'unnamed' }
105
+ raise MCPClient::Errors::AmbiguousToolName,
106
+ "Multiple tools named '#{tool_name}' found across servers (#{server_names.join(', ')}). " \
107
+ "Please specify a server using the 'server' parameter."
108
+ end
109
+
110
+ tool = matching_tools.first
111
+ end
68
112
 
69
113
  # Validate parameters against tool schema
70
114
  validate_params!(tool, parameters)
71
115
 
72
- # Find the server that owns this tool
73
- server = find_server_for_tool(tool)
116
+ # Use the tool's associated server
117
+ server = tool.server
74
118
  raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless server
75
119
 
76
- server.call_tool(tool_name, parameters)
120
+ begin
121
+ server.call_tool(tool_name, parameters)
122
+ rescue MCPClient::Errors::ConnectionError => e
123
+ # Add server identity information to the error for better context
124
+ server_id = server.name ? "#{server.class}[#{server.name}]" : server.class.name
125
+ raise MCPClient::Errors::ToolCallError, "Error calling tool '#{tool_name}': #{e.message} (Server: #{server_id})"
126
+ end
77
127
  end
78
128
 
79
129
  # Convert MCP tools to OpenAI function specifications
@@ -94,6 +144,9 @@ module MCPClient
94
144
  tools.map(&:to_anthropic_tool)
95
145
  end
96
146
 
147
+ # Convert MCP tools to Google Vertex AI tool specifications
148
+ # @param tool_names [Array<String>, nil] optional list of tool names to include, nil means all tools
149
+ # @return [Array<Hash>] Google Vertex AI tool specifications with cleaned schemas
97
150
  def to_google_tools(tool_names: nil)
98
151
  tools = list_tools
99
152
  tools = tools.select { |t| tool_names.include?(t.name) } if tool_names
@@ -118,6 +171,13 @@ module MCPClient
118
171
  @notification_listeners << block
119
172
  end
120
173
 
174
+ # Find a server by name
175
+ # @param name [String] the name of the server to find
176
+ # @return [MCPClient::ServerBase, nil] the server with the given name, or nil if not found
177
+ def find_server(name)
178
+ @servers.find { |s| s.name == name }
179
+ end
180
+
121
181
  # Find all tools whose name matches the given pattern (String or Regexp)
122
182
  # @param pattern [String, Regexp] pattern to match tool names
123
183
  # @return [Array<MCPClient::Tool>] matching tools
@@ -134,13 +194,15 @@ module MCPClient
134
194
  end
135
195
 
136
196
  # Call multiple tools in batch
137
- # @param calls [Array<Hash>] array of calls in the form { name: tool_name, parameters: {...} }
197
+ # @param calls [Array<Hash>] array of calls in the form:
198
+ # { name: tool_name, parameters: {...}, server: optional_server_name }
138
199
  # @return [Array<Object>] array of results for each tool invocation
139
200
  def call_tools(calls)
140
201
  calls.map do |call|
141
202
  name = call[:name] || call['name']
142
203
  params = call[:parameters] || call['parameters'] || {}
143
- call_tool(name, params)
204
+ server = call[:server] || call['server']
205
+ call_tool(name, params, server: server)
144
206
  end
145
207
  end
146
208
 
@@ -148,24 +210,52 @@ module MCPClient
148
210
  # Returns an Enumerator yielding streaming updates if supported.
149
211
  # @param tool_name [String] the name of the tool to call
150
212
  # @param parameters [Hash] the parameters to pass to the tool
213
+ # @param server [String, Symbol, Integer, MCPClient::ServerBase, nil] optional server to use
151
214
  # @return [Enumerator] streaming enumerator or single-value enumerator
152
- def call_tool_streaming(tool_name, parameters)
215
+ def call_tool_streaming(tool_name, parameters, server: nil)
153
216
  tools = list_tools
154
- tool = tools.find { |t| t.name == tool_name }
155
- raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found" unless tool
217
+
218
+ if server
219
+ # Use the specified server
220
+ srv = select_server(server)
221
+ # Find the tool on this specific server
222
+ tool = tools.find { |t| t.name == tool_name && t.server == srv }
223
+ unless tool
224
+ raise MCPClient::Errors::ToolNotFound,
225
+ "Tool '#{tool_name}' not found on server '#{srv.name || srv.class.name}'"
226
+ end
227
+ else
228
+ # Find the tool across all servers
229
+ matching_tools = tools.select { |t| t.name == tool_name }
230
+
231
+ if matching_tools.empty?
232
+ raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found"
233
+ elsif matching_tools.size > 1
234
+ # If multiple matches, disambiguate with server names
235
+ server_names = matching_tools.map { |t| t.server&.name || 'unnamed' }
236
+ raise MCPClient::Errors::AmbiguousToolName,
237
+ "Multiple tools named '#{tool_name}' found across servers (#{server_names.join(', ')}). " \
238
+ "Please specify a server using the 'server' parameter."
239
+ end
240
+
241
+ tool = matching_tools.first
242
+ end
156
243
 
157
244
  # Validate parameters against tool schema
158
245
  validate_params!(tool, parameters)
159
- # Find the server that owns this tool
160
- server = find_server_for_tool(tool)
246
+
247
+ # Use the tool's associated server
248
+ server = tool.server
161
249
  raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless server
162
250
 
163
- if server.respond_to?(:call_tool_streaming)
251
+ begin
252
+ # Use the streaming API if it's available
164
253
  server.call_tool_streaming(tool_name, parameters)
165
- else
166
- Enumerator.new do |yielder|
167
- yielder << server.call_tool(tool_name, parameters)
168
- end
254
+ rescue MCPClient::Errors::ConnectionError => e
255
+ # Add server identity information to the error for better context
256
+ server_id = server.name ? "#{server.class}[#{server.name}]" : server.class.name
257
+ msg = "Error calling streaming tool '#{tool_name}': #{e.message} (Server: #{server_id})"
258
+ raise MCPClient::Errors::ToolCallError, msg
169
259
  end
170
260
  end
171
261
 
@@ -218,20 +308,24 @@ module MCPClient
218
308
  # @param params [Hash] parameters for the notification
219
309
  # @return [void]
220
310
  def process_notification(server, method, params)
311
+ server_id = server.name ? "#{server.class}[#{server.name}]" : server.class
221
312
  case method
222
313
  when 'notifications/tools/list_changed'
223
- logger.warn("[#{server.class}] Tool list has changed, clearing tool cache")
314
+ logger.warn("[#{server_id}] Tool list has changed, clearing tool cache")
224
315
  clear_cache
225
316
  when 'notifications/resources/updated'
226
- logger.warn("[#{server.class}] Resource #{params['uri']} updated")
317
+ logger.warn("[#{server_id}] Resource #{params['uri']} updated")
227
318
  when 'notifications/prompts/list_changed'
228
- logger.warn("[#{server.class}] Prompt list has changed")
319
+ logger.warn("[#{server_id}] Prompt list has changed")
229
320
  when 'notifications/resources/list_changed'
230
- logger.warn("[#{server.class}] Resource list has changed")
321
+ logger.warn("[#{server_id}] Resource list has changed")
322
+ else
323
+ # Log unknown notification types for debugging purposes
324
+ logger.debug("[#{server_id}] Received unknown notification: #{method} - #{params}")
231
325
  end
232
326
  end
233
327
 
234
- # Select a server based on index, type, or instance
328
+ # Select a server based on index, name, type, or instance
235
329
  # @param server_arg [Integer, String, Symbol, MCPClient::ServerBase, nil] server selector
236
330
  # @return [MCPClient::ServerBase]
237
331
  def select_server(server_arg)
@@ -246,15 +340,20 @@ module MCPClient
246
340
  end
247
341
  when String, Symbol
248
342
  key = server_arg.to_s.downcase
343
+
344
+ # First check if it's a server name match
345
+ srv = @servers.find { |s| s.name && s.name.downcase == key }
346
+ return srv if srv
347
+
348
+ # Then check if it's a server type match
249
349
  srv = @servers.find { |s| s.class.name.split('::').last.downcase.end_with?(key) }
250
- raise MCPClient::Errors::ServerNotFound, "Server of type #{server_arg} not found" unless srv
350
+ raise MCPClient::Errors::ServerNotFound, "Server with name or type '#{server_arg}' not found" unless srv
251
351
 
252
352
  srv
253
353
  else
254
354
  raise ArgumentError, "Invalid server argument: #{server_arg.inspect}" unless @servers.include?(server_arg)
255
355
 
256
356
  server_arg
257
-
258
357
  end
259
358
  end
260
359
 
@@ -30,7 +30,11 @@ module MCPClient
30
30
  next unless validate_server_config(config, server_name)
31
31
 
32
32
  server_config = process_server_config(config, server_name)
33
- result[server_name] = server_config if server_config
33
+ next unless server_config
34
+
35
+ # Add server name to the config
36
+ server_config[:name] = server_name
37
+ result[server_name] = server_config
34
38
  end
35
39
 
36
40
  result
@@ -23,7 +23,11 @@ module MCPClient
23
23
 
24
24
  # Raised when there's an error in the MCP server transport
25
25
  class TransportError < MCPError; end
26
+
26
27
  # Raised when tool parameters fail validation against JSON schema
27
28
  class ValidationError < MCPError; end
29
+
30
+ # Raised when multiple tools with the same name exist across different servers
31
+ class AmbiguousToolName < MCPError; end
28
32
  end
29
33
  end
@@ -3,6 +3,16 @@
3
3
  module MCPClient
4
4
  # Base class for MCP servers - serves as the interface for different server implementations
5
5
  class ServerBase
6
+ # @!attribute [r] name
7
+ # @return [String] the name of the server
8
+ attr_reader :name
9
+
10
+ # Initialize the server with a name
11
+ # @param name [String, nil] server name
12
+ def initialize(name: nil)
13
+ @name = name
14
+ end
15
+
6
16
  # Initialize a connection to the MCP server
7
17
  # @return [Boolean] true if connection successful
8
18
  def connect
@@ -45,6 +55,16 @@ module MCPClient
45
55
  raise NotImplementedError, 'Subclasses must implement rpc_notify'
46
56
  end
47
57
 
58
+ # Stream a tool call result (default implementation returns single-value stream)
59
+ # @param tool_name [String] the name of the tool to call
60
+ # @param parameters [Hash] the parameters to pass to the tool
61
+ # @return [Enumerator] stream of results
62
+ def call_tool_streaming(tool_name, parameters)
63
+ Enumerator.new do |yielder|
64
+ yielder << call_tool(tool_name, parameters)
65
+ end
66
+ end
67
+
48
68
  # Ping the MCP server to check connectivity (zero-parameter heartbeat call)
49
69
  # @return [Object] result from the ping request
50
70
  def ping
@@ -5,8 +5,9 @@ module MCPClient
5
5
  class ServerFactory
6
6
  # Create a server instance based on configuration
7
7
  # @param config [Hash] server configuration
8
+ # @param logger [Logger, nil] optional logger to use for the server
8
9
  # @return [MCPClient::ServerBase] server instance
9
- def self.create(config)
10
+ def self.create(config, logger: nil)
10
11
  case config[:type]
11
12
  when 'stdio'
12
13
  MCPClient::ServerStdio.new(
@@ -14,7 +15,8 @@ module MCPClient
14
15
  retries: config[:retries] || 0,
15
16
  retry_backoff: config[:retry_backoff] || 1,
16
17
  read_timeout: config[:read_timeout] || MCPClient::ServerStdio::READ_TIMEOUT,
17
- logger: config[:logger]
18
+ name: config[:name],
19
+ logger: config[:logger] || logger
18
20
  )
19
21
  when 'sse'
20
22
  MCPClient::ServerSSE.new(
@@ -24,7 +26,8 @@ module MCPClient
24
26
  ping: config[:ping] || 10,
25
27
  retries: config[:retries] || 0,
26
28
  retry_backoff: config[:retry_backoff] || 1,
27
- logger: config[:logger]
29
+ name: config[:name],
30
+ logger: config[:logger] || logger
28
31
  )
29
32
  else
30
33
  raise ArgumentError, "Unknown server type: #{config[:type]}"