Class: MCPClient::Client
- Inherits:
-
Object
- Object
- MCPClient::Client
- Defined in:
- lib/mcp_client/client.rb
Overview
MCP Client for integrating with the Model Context Protocol This is the main entry point for using MCP tools
Instance Attribute Summary collapse
-
#logger ⇒ Logger
readonly
Logger for client operations.
-
#prompt_cache ⇒ Hash<String, MCPClient::Prompt>
readonly
Cache of prompts by composite key (server_id:name).
-
#resource_cache ⇒ Hash<String, MCPClient::Resource>
readonly
Cache of resources by composite key (server_id:uri).
-
#roots ⇒ Object
Returns the value of attribute roots.
-
#servers ⇒ Array<MCPClient::ServerBase>
readonly
List of servers.
-
#tool_cache ⇒ Hash<String, MCPClient::Tool>
readonly
Cache of tools by composite key (server_id:name).
Instance Method Summary collapse
-
#call_tool(tool_name, parameters, server: nil) ⇒ Object
Calls a specific tool by name with the given parameters.
-
#call_tool_as_task(tool_name, parameters, ttl: nil, server: nil) ⇒ MCPClient::Task
Call a tool as a task (task-augmented tools/call, MCP 2025-11-25).
-
#call_tool_streaming(tool_name, parameters, server: nil) ⇒ Enumerator
Stream call of a specific tool by name with the given parameters.
-
#call_tools(calls) ⇒ Array<Object>
Call multiple tools in batch.
-
#cancel_task(task_id, server: nil) ⇒ MCPClient::Task
Cancel a task (tasks/cancel, MCP 2025-11-25).
-
#cleanup ⇒ Object
Clean up all server connections.
-
#clear_cache ⇒ void
Clear the cached tools so that next list_tools will fetch fresh data.
-
#complete(ref:, argument:, context: nil, server: nil) ⇒ Hash
Request completion suggestions from a server (MCP 2025-06-18).
-
#find_server(name) ⇒ MCPClient::ServerBase?
Find a server by name.
-
#find_tool(pattern) ⇒ MCPClient::Tool?
Find the first tool whose name matches the given pattern.
-
#find_tools(pattern) ⇒ Array<MCPClient::Tool>
Find all tools whose name matches the given pattern (String or Regexp).
-
#get_prompt(prompt_name, parameters, server: nil) ⇒ Object
Gets a specific prompt by name with the given parameters.
-
#get_task(task_id, server: nil) ⇒ MCPClient::Task
Get the current state of a task (tasks/get, MCP 2025-11-25).
-
#get_task_result(task_id, server: nil) ⇒ Object
Retrieve the result of a completed task (tasks/result, MCP 2025-11-25).
-
#initialize(mcp_server_configs: [], logger: nil, elicitation_handler: nil, roots: nil, sampling_handler: nil) ⇒ Client
constructor
Initialize a new MCPClient::Client.
-
#list_prompts(cache: true) ⇒ Array<MCPClient::Prompt>
Lists all available prompts from all connected MCP servers.
-
#list_resources(cache: true, cursor: nil) ⇒ Hash
Lists all available resources from all connected MCP servers.
-
#list_tasks(cursor: nil, server: nil) ⇒ Hash
List tasks known to a server (tasks/list, paginated, MCP 2025-11-25).
-
#list_tools(cache: true) ⇒ Array<MCPClient::Tool>
Lists all available tools from all connected MCP servers.
-
#log_level=(level) ⇒ Array<Hash>
Set the logging level on all connected servers (MCP 2025-06-18) To set on a specific server, use: client.find_server('name').log_level = 'debug'.
-
#on_notification {|server, method, params| ... } ⇒ void
Register a callback for JSON-RPC notifications from servers.
-
#ping(server_index: nil) ⇒ Object
Ping the MCP server to check connectivity (zero-parameter heartbeat call).
-
#read_resource(uri, server: nil) ⇒ Object
Reads a specific resource by URI.
-
#send_notification(method, params: {}, server: nil) ⇒ void
Send a raw JSON-RPC notification to a server (no response expected).
-
#send_rpc(method, params: {}, server: nil) ⇒ Object
Send a raw JSON-RPC request to a server.
-
#to_anthropic_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to Anthropic Claude tool specifications.
-
#to_google_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to Google Vertex AI tool specifications.
-
#to_openai_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to OpenAI function specifications.
Constructor Details
#initialize(mcp_server_configs: [], logger: nil, elicitation_handler: nil, roots: nil, sampling_handler: nil) ⇒ Client
Initialize a new MCPClient::Client
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/mcp_client/client.rb', line 29 def initialize(mcp_server_configs: [], logger: nil, elicitation_handler: nil, roots: nil, sampling_handler: nil) # Preserve a caller-supplied logger's formatter (only tag progname), and # install the default formatter solely on a logger we create ourselves. # Overwriting the formatter of an application's logger would silently # reformat every log line it emits elsewhere. if logger @logger = logger @logger.progname = self.class.name else @logger = Logger.new($stdout, level: Logger::WARN) @logger.progname = self.class.name @logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" } end @servers = mcp_server_configs.map do |config| @logger.debug("Creating server with config: #{config.inspect}") MCPClient::ServerFactory.create(config, logger: @logger) end @tool_cache = {} @prompt_cache = {} @resource_cache = {} # JSON-RPC notification listeners @notification_listeners = [] # Elicitation handler (MCP 2025-06-18) @elicitation_handler = elicitation_handler # Sampling handler (MCP 2025-11-25) @sampling_handler = sampling_handler # Roots (MCP 2025-06-18) @roots = normalize_roots(roots) # Register default and user-defined notification handlers on each server @servers.each do |server| server.on_notification do |method, params| # Default notification processing (e.g., cache invalidation, logging) process_notification(server, method, params) # Invoke user-defined listeners @notification_listeners.each { |cb| cb.call(server, method, params) } end # Register elicitation handler on each server if server.respond_to?(:on_elicitation_request) server.on_elicitation_request(&method(:handle_elicitation_request)) end # Register roots list handler on each server (MCP 2025-06-18) server.on_roots_list_request(&method(:handle_roots_list_request)) if server.respond_to?(:on_roots_list_request) # Register sampling handler on each server (MCP 2025-11-25) server.on_sampling_request(&method(:handle_sampling_request)) if server.respond_to?(:on_sampling_request) end end |
Instance Attribute Details
#logger ⇒ Logger (readonly)
Returns logger for client operations.
21 |
# File 'lib/mcp_client/client.rb', line 21 attr_reader :servers, :tool_cache, :prompt_cache, :resource_cache, :logger, :roots |
#prompt_cache ⇒ Hash<String, MCPClient::Prompt> (readonly)
Returns cache of prompts by composite key (server_id:name).
21 |
# File 'lib/mcp_client/client.rb', line 21 attr_reader :servers, :tool_cache, :prompt_cache, :resource_cache, :logger, :roots |
#resource_cache ⇒ Hash<String, MCPClient::Resource> (readonly)
Returns cache of resources by composite key (server_id:uri).
21 |
# File 'lib/mcp_client/client.rb', line 21 attr_reader :servers, :tool_cache, :prompt_cache, :resource_cache, :logger, :roots |
#roots ⇒ Object
Returns the value of attribute roots.
21 |
# File 'lib/mcp_client/client.rb', line 21 attr_reader :servers, :tool_cache, :prompt_cache, :resource_cache, :logger, :roots |
#servers ⇒ Array<MCPClient::ServerBase> (readonly)
Returns list of servers.
21 22 23 |
# File 'lib/mcp_client/client.rb', line 21 def servers @servers end |
#tool_cache ⇒ Hash<String, MCPClient::Tool> (readonly)
Returns cache of tools by composite key (server_id:name).
21 |
# File 'lib/mcp_client/client.rb', line 21 attr_reader :servers, :tool_cache, :prompt_cache, :resource_cache, :logger, :roots |
Instance Method Details
#call_tool(tool_name, parameters, server: nil) ⇒ Object
Calls a specific tool by name with the given parameters
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/mcp_client/client.rb', line 262 def call_tool(tool_name, parameters, server: nil) tools = list_tools if server # Use the specified server srv = select_server(server) # Find the tool on this specific server tool = tools.find { |t| t.name == tool_name && t.server == srv } unless tool raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found on server '#{srv.name || srv.class.name}'" end else # Find the tool across all servers matching_tools = tools.select { |t| t.name == tool_name } if matching_tools.empty? raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found" elsif matching_tools.size > 1 # If multiple matches, disambiguate with server names server_names = matching_tools.map { |t| t.server&.name || 'unnamed' } raise MCPClient::Errors::AmbiguousToolName, "Multiple tools named '#{tool_name}' found across servers (#{server_names.join(', ')}). " \ "Please specify a server using the 'server' parameter." end tool = matching_tools.first end # Validate parameters against tool schema validate_params!(tool, parameters) reject_task_required!(tool, tool_name) # Use the tool's associated server server = tool.server raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless server begin server.call_tool(tool_name, parameters) rescue MCPClient::Errors::ConnectionError => e # Add server identity information to the error for better context server_id = server.name ? "#{server.class}[#{server.name}]" : server.class.name raise MCPClient::Errors::ToolCallError, "Error calling tool '#{tool_name}': #{e.} (Server: #{server_id})" end end |
#call_tool_as_task(tool_name, parameters, ttl: nil, server: nil) ⇒ MCPClient::Task
Call a tool as a task (task-augmented tools/call, MCP 2025-11-25).
Instead of blocking for the result, the server accepts the request and immediately returns a task handle; the actual result is retrieved later via #get_task_result once the task reaches a terminal status. The server must advertise the tasks.requests.tools.call capability, and the tool must declare execution.taskSupport of 'optional' or 'required'.
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 |
# File 'lib/mcp_client/client.rb', line 526 def call_tool_as_task(tool_name, parameters, ttl: nil, server: nil) tool = resolve_tool(tool_name, server: server) validate_params!(tool, parameters) srv = tool.server raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless srv unless server_supports_task_tool_call?(srv) raise MCPClient::Errors::TaskError, 'Server does not support task-augmented tools/call (no tasks.requests.tools.call capability)' end unless tool.supports_task? raise MCPClient::Errors::TaskError, "Tool '#{tool_name}' does not support task execution (execution.taskSupport is forbidden/unset)" end task_params = {} task_params[:ttl] = ttl if ttl # Keep _meta (string or symbol key) as a top-level request field rather # than a tool argument, so request metadata is preserved and does not fail # tool input-schema validation. = [:_meta, '_meta'].find { |k| parameters.key?(k) } arguments = ? parameters.reject { |k, _| k == } : parameters rpc_params = { name: tool_name, arguments: arguments, task: task_params } rpc_params[:_meta] = parameters[] if begin result = srv.rpc_request('tools/call', rpc_params) MCPClient::Task.from_create_result(result, server: srv) rescue MCPClient::Errors::ServerError, MCPClient::Errors::TransportError, MCPClient::Errors::ConnectionError => e raise MCPClient::Errors::TaskError, "Error creating task for tool '#{tool_name}': #{e.}" end end |
#call_tool_streaming(tool_name, parameters, server: nil) ⇒ Enumerator
Stream call of a specific tool by name with the given parameters. Returns an Enumerator yielding streaming updates if supported.
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'lib/mcp_client/client.rb', line 408 def call_tool_streaming(tool_name, parameters, server: nil) tools = list_tools if server # Use the specified server srv = select_server(server) # Find the tool on this specific server tool = tools.find { |t| t.name == tool_name && t.server == srv } unless tool raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found on server '#{srv.name || srv.class.name}'" end else # Find the tool across all servers matching_tools = tools.select { |t| t.name == tool_name } if matching_tools.empty? raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found" elsif matching_tools.size > 1 # If multiple matches, disambiguate with server names server_names = matching_tools.map { |t| t.server&.name || 'unnamed' } raise MCPClient::Errors::AmbiguousToolName, "Multiple tools named '#{tool_name}' found across servers (#{server_names.join(', ')}). " \ "Please specify a server using the 'server' parameter." end tool = matching_tools.first end # Validate parameters against tool schema validate_params!(tool, parameters) reject_task_required!(tool, tool_name) # Use the tool's associated server server = tool.server raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless server begin # Use the streaming API if it's available server.call_tool_streaming(tool_name, parameters) rescue MCPClient::Errors::ConnectionError => e # Add server identity information to the error for better context server_id = server.name ? "#{server.class}[#{server.name}]" : server.class.name msg = "Error calling streaming tool '#{tool_name}': #{e.} (Server: #{server_id})" raise MCPClient::Errors::ToolCallError, msg end end |
#call_tools(calls) ⇒ Array<Object>
Call multiple tools in batch
393 394 395 396 397 398 399 400 |
# File 'lib/mcp_client/client.rb', line 393 def call_tools(calls) calls.map do |call| name = call[:name] || call['name'] params = call[:parameters] || call['parameters'] || {} server = call[:server] || call['server'] call_tool(name, params, server: server) end end |
#cancel_task(task_id, server: nil) ⇒ MCPClient::Task
Cancel a task (tasks/cancel, MCP 2025-11-25)
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 |
# File 'lib/mcp_client/client.rb', line 626 def cancel_task(task_id, server: nil) srv = select_server(server) begin result = srv.rpc_request('tasks/cancel', { taskId: task_id }) MCPClient::Task.from_json(result, server: srv) rescue MCPClient::Errors::ServerError => e # A terminal task cannot be cancelled (-32602); that is an error, not a # missing task, so keep it as a TaskError. if e..match?(/terminal/i) raise MCPClient::Errors::TaskError, "Error cancelling task '#{task_id}': #{e.}" end raise task_error_from(e, task_id, 'cancelling') rescue MCPClient::Errors::TransportError, MCPClient::Errors::ConnectionError => e raise MCPClient::Errors::TaskError, "Error cancelling task '#{task_id}': #{e.}" end end |
#cleanup ⇒ Object
Clean up all server connections
336 337 338 |
# File 'lib/mcp_client/client.rb', line 336 def cleanup servers.each(&:cleanup) end |
#clear_cache ⇒ void
This method returns an undefined value.
Clear the cached tools so that next list_tools will fetch fresh data
342 343 344 345 346 |
# File 'lib/mcp_client/client.rb', line 342 def clear_cache @tool_cache.clear @prompt_cache.clear @resource_cache.clear end |
#complete(ref:, argument:, context: nil, server: nil) ⇒ Hash
Request completion suggestions from a server (MCP 2025-06-18)
506 507 508 509 |
# File 'lib/mcp_client/client.rb', line 506 def complete(ref:, argument:, context: nil, server: nil) srv = select_server(server) srv.complete(ref: ref, argument: argument, context: context) end |
#find_server(name) ⇒ MCPClient::ServerBase?
Find a server by name
368 369 370 |
# File 'lib/mcp_client/client.rb', line 368 def find_server(name) @servers.find { |s| s.name == name } end |
#find_tool(pattern) ⇒ MCPClient::Tool?
Find the first tool whose name matches the given pattern
383 384 385 |
# File 'lib/mcp_client/client.rb', line 383 def find_tool(pattern) find_tools(pattern).first end |
#find_tools(pattern) ⇒ Array<MCPClient::Tool>
Find all tools whose name matches the given pattern (String or Regexp)
375 376 377 378 |
# File 'lib/mcp_client/client.rb', line 375 def find_tools(pattern) rx = pattern.is_a?(Regexp) ? pattern : /#{Regexp.escape(pattern)}/ list_tools.select { |t| t.name.match(rx) } end |
#get_prompt(prompt_name, parameters, server: nil) ⇒ Object
Gets a specific prompt by name with the given parameters
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/mcp_client/client.rb', line 112 def get_prompt(prompt_name, parameters, server: nil) prompts = list_prompts if server # Use the specified server srv = select_server(server) # Find the prompt on this specific server prompt = prompts.find { |t| t.name == prompt_name && t.server == srv } unless prompt raise MCPClient::Errors::PromptNotFound, "Prompt '#{prompt_name}' not found on server '#{srv.name || srv.class.name}'" end else # Find the prompt across all servers matching_prompts = prompts.select { |t| t.name == prompt_name } if matching_prompts.empty? raise MCPClient::Errors::PromptNotFound, "Prompt '#{prompt_name}' not found" elsif matching_prompts.size > 1 # If multiple matches, disambiguate with server names server_names = matching_prompts.map { |t| t.server&.name || 'unnamed' } raise MCPClient::Errors::AmbiguousPromptName, "Multiple prompts named '#{prompt_name}' found across servers (#{server_names.join(', ')}). " \ "Please specify a server using the 'server' parameter." end prompt = matching_prompts.first end # Use the prompt's associated server server = prompt.server raise MCPClient::Errors::ServerNotFound, "No server found for prompt '#{prompt_name}'" unless server begin server.get_prompt(prompt_name, parameters) rescue MCPClient::Errors::ConnectionError => e # Add server identity information to the error for better context server_id = server.name ? "#{server.class}[#{server.name}]" : server.class.name raise MCPClient::Errors::PromptGetError, "Error getting prompt '#{prompt_name}': #{e.} (Server: #{server_id})" end end |
#get_task(task_id, server: nil) ⇒ MCPClient::Task
Get the current state of a task (tasks/get, MCP 2025-11-25)
567 568 569 570 571 572 573 574 575 576 577 578 |
# File 'lib/mcp_client/client.rb', line 567 def get_task(task_id, server: nil) srv = select_server(server) begin result = srv.rpc_request('tasks/get', { taskId: task_id }) MCPClient::Task.from_json(result, server: srv) rescue MCPClient::Errors::ServerError => e raise task_error_from(e, task_id, 'getting') rescue MCPClient::Errors::TransportError, MCPClient::Errors::ConnectionError => e raise MCPClient::Errors::TaskError, "Error getting task '#{task_id}': #{e.}" end end |
#get_task_result(task_id, server: nil) ⇒ Object
Retrieve the result of a completed task (tasks/result, MCP 2025-11-25). Returns exactly what the underlying request would have returned (e.g. a CallToolResult hash with 'content'/'isError'/'structuredContent'); it is NOT wrapped in a Task. Blocks on the server until the task is terminal.
589 590 591 592 593 594 595 596 597 598 599 |
# File 'lib/mcp_client/client.rb', line 589 def get_task_result(task_id, server: nil) srv = select_server(server) begin srv.rpc_request('tasks/result', { taskId: task_id }) rescue MCPClient::Errors::ServerError => e raise task_error_from(e, task_id, 'getting result for') rescue MCPClient::Errors::TransportError, MCPClient::Errors::ConnectionError => e raise MCPClient::Errors::TaskError, "Error getting result for task '#{task_id}': #{e.}" end end |
#list_prompts(cache: true) ⇒ Array<MCPClient::Prompt>
Lists all available prompts from all connected MCP servers
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/mcp_client/client.rb', line 81 def list_prompts(cache: true) return @prompt_cache.values if cache && !@prompt_cache.empty? prompts = [] connection_errors = [] servers.each do |server| server.list_prompts.each do |prompt| cache_key = cache_key_for(server, prompt.name) @prompt_cache[cache_key] = prompt prompts << prompt end rescue MCPClient::Errors::ConnectionError => e # Fast-fail on authorization errors for better user experience # If this is the first server or we haven't collected any prompts yet, # raise the auth error directly to avoid cascading error messages raise e if e..include?('Authorization failed') && prompts.empty? # Store the error and try other servers connection_errors << e @logger.error("Server error: #{e.}") end prompts end |
#list_resources(cache: true, cursor: nil) ⇒ Hash
Lists all available resources from all connected MCP servers
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/mcp_client/client.rb', line 161 def list_resources(cache: true, cursor: nil) # If cursor is provided, we can only query one server (the one that provided the cursor) # This is a limitation of aggregating multiple servers if cursor # For now, just use the first server when cursor is provided # In a real implementation, you'd need to track which server the cursor came from return servers.first.list_resources(cursor: cursor) if servers.any? return { 'resources' => [], 'nextCursor' => nil } end # Use cache if available and no cursor return { 'resources' => @resource_cache.values, 'nextCursor' => nil } if cache && !@resource_cache.empty? resources = [] connection_errors = [] servers.each do |server| result = server.list_resources resource_list = result['resources'] || [] resource_list.each do |resource| cache_key = cache_key_for(server, resource.uri) @resource_cache[cache_key] = resource resources << resource end rescue MCPClient::Errors::ConnectionError => e # Fast-fail on authorization errors for better user experience # If this is the first server or we haven't collected any resources yet, # raise the auth error directly to avoid cascading error messages raise e if e..include?('Authorization failed') && resources.empty? # Store the error and try other servers connection_errors << e @logger.error("Server error: #{e.}") end # Return hash format consistent with server methods { 'resources' => resources, 'nextCursor' => nil } end |
#list_tasks(cursor: nil, server: nil) ⇒ Hash
List tasks known to a server (tasks/list, paginated, MCP 2025-11-25)
606 607 608 609 610 611 612 613 614 615 616 617 |
# File 'lib/mcp_client/client.rb', line 606 def list_tasks(cursor: nil, server: nil) srv = select_server(server) params = cursor ? { cursor: cursor } : {} begin result = srv.rpc_request('tasks/list', params) || {} tasks = (result['tasks'] || []).map { |t| MCPClient::Task.from_json(t, server: srv) } { tasks: tasks, next_cursor: result['nextCursor'] } rescue MCPClient::Errors::ServerError, MCPClient::Errors::TransportError, MCPClient::Errors::ConnectionError => e raise MCPClient::Errors::TaskError, "Error listing tasks: #{e.}" end end |
#list_tools(cache: true) ⇒ Array<MCPClient::Tool>
Lists all available tools from all connected MCP servers
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/mcp_client/client.rb', line 224 def list_tools(cache: true) return @tool_cache.values if cache && !@tool_cache.empty? tools = [] connection_errors = [] servers.each do |server| server.list_tools.each do |tool| cache_key = cache_key_for(server, tool.name) @tool_cache[cache_key] = tool tools << tool end rescue MCPClient::Errors::ConnectionError => e # Fast-fail on authorization errors for better user experience # If this is the first server or we haven't collected any tools yet, # raise the auth error directly to avoid cascading error messages raise e if e..include?('Authorization failed') && tools.empty? # Store the error and try other servers connection_errors << e @logger.error("Server error: #{e.}") end # If we didn't get any tools from any server but have servers configured, report failure if tools.empty? && !servers.empty? raise connection_errors.first if connection_errors.any? @logger.warn('No tools found from any server.') end tools end |
#log_level=(level) ⇒ Array<Hash>
Set the logging level on all connected servers (MCP 2025-06-18) To set on a specific server, use: client.find_server('name').log_level = 'debug'
651 652 653 |
# File 'lib/mcp_client/client.rb', line 651 def log_level=(level) @servers.map { |srv| srv.log_level = level } end |
#on_notification {|server, method, params| ... } ⇒ void
This method returns an undefined value.
Register a callback for JSON-RPC notifications from servers
351 352 353 |
# File 'lib/mcp_client/client.rb', line 351 def on_notification(&block) @notification_listeners << block end |
#ping(server_index: nil) ⇒ Object
Ping the MCP server to check connectivity (zero-parameter heartbeat call)
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/mcp_client/client.rb', line 460 def ping(server_index: nil) if server_index.nil? # Ping first available server raise MCPClient::Errors::ServerNotFound, 'No server available for ping' if @servers.empty? @servers.first.ping else # Ping specified server if server_index >= @servers.length raise MCPClient::Errors::ServerNotFound, "Server at index #{server_index} not found" end @servers[server_index].ping end end |
#read_resource(uri, server: nil) ⇒ Object
Reads a specific resource by URI
206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/mcp_client/client.rb', line 206 def read_resource(uri, server: nil) result = list_resources resources = result['resources'] || [] resource = if server find_resource_on_server(uri, resources, server) else find_resource_across_servers(uri, resources) end execute_resource_read(resource, uri) end |
#send_notification(method, params: {}, server: nil) ⇒ void
This method returns an undefined value.
Send a raw JSON-RPC notification to a server (no response expected)
492 493 494 495 |
# File 'lib/mcp_client/client.rb', line 492 def send_notification(method, params: {}, server: nil) srv = select_server(server) srv.rpc_notify(method, params) end |
#send_rpc(method, params: {}, server: nil) ⇒ Object
Send a raw JSON-RPC request to a server
482 483 484 485 |
# File 'lib/mcp_client/client.rb', line 482 def send_rpc(method, params: {}, server: nil) srv = select_server(server) srv.rpc_request(method, params) end |
#to_anthropic_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to Anthropic Claude tool specifications
320 321 322 323 324 |
# File 'lib/mcp_client/client.rb', line 320 def to_anthropic_tools(tool_names: nil) tools = list_tools tools = tools.select { |t| tool_names.include?(t.name) } if tool_names tools.map(&:to_anthropic_tool) end |
#to_google_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to Google Vertex AI tool specifications
329 330 331 332 333 |
# File 'lib/mcp_client/client.rb', line 329 def to_google_tools(tool_names: nil) tools = list_tools tools = tools.select { |t| tool_names.include?(t.name) } if tool_names tools.map(&:to_google_tool) end |
#to_openai_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to OpenAI function specifications
311 312 313 314 315 |
# File 'lib/mcp_client/client.rb', line 311 def to_openai_tools(tool_names: nil) tools = list_tools tools = tools.select { |t| tool_names.include?(t.name) } if tool_names tools.map(&:to_openai_tool) end |