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 ⇒ Object
readonly
Returns the value of attribute logger.
-
#servers ⇒ Array<MCPClient::ServerBase>
readonly
List of servers.
-
#tool_cache ⇒ Hash<String, MCPClient::Tool>
readonly
Cache of tools by name.
Instance Method Summary collapse
-
#call_tool(tool_name, parameters) ⇒ Object
Calls a specific tool by name with the given parameters.
-
#call_tool_streaming(tool_name, parameters) ⇒ Enumerator
Stream call of a specific tool by name with the given parameters.
-
#call_tools(calls) ⇒ Array<Object>
Call multiple tools in batch.
-
#cleanup ⇒ Object
Clean up all server connections.
-
#clear_cache ⇒ void
Clear the cached tools so that next list_tools will fetch fresh data.
-
#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).
-
#initialize(mcp_server_configs: [], logger: nil) ⇒ Client
constructor
Initialize a new MCPClient::Client.
-
#list_tools(cache: true) ⇒ Array<MCPClient::Tool>
Lists all available tools from all connected MCP servers.
-
#on_notification {|server, method, params| ... } ⇒ void
Register a callback for JSON-RPC notifications from servers.
-
#ping(params = {}, server_index: nil) ⇒ Object
Ping the MCP server to check connectivity.
-
#to_anthropic_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to Anthropic Claude 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) ⇒ Client
Initialize a new MCPClient::Client
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/mcp_client/client.rb', line 20 def initialize(mcp_server_configs: [], logger: nil) @logger = logger || Logger.new($stdout, level: Logger::WARN) @logger.progname = self.class.name @logger.formatter = proc { |severity, _datetime, progname, msg| "#{severity} [#{progname}] #{msg}\n" } @servers = mcp_server_configs.map do |config| @logger.debug("Creating server with config: #{config.inspect}") MCPClient::ServerFactory.create(config) end @tool_cache = {} # JSON-RPC notification listeners @notification_listeners = [] # Register default and user-defined notification handlers on each server @servers.each do |server| server.on_notification do |method, params| # Default handling: clear tool cache on tools list change clear_cache if method == 'notifications/tools/list_changed' # Invoke user listeners @notification_listeners.each { |cb| cb.call(server, method, params) } end end end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
15 |
# File 'lib/mcp_client/client.rb', line 15 attr_reader :servers, :tool_cache, :logger |
#servers ⇒ Array<MCPClient::ServerBase> (readonly)
Returns list of servers.
15 16 17 |
# File 'lib/mcp_client/client.rb', line 15 def servers @servers end |
#tool_cache ⇒ Hash<String, MCPClient::Tool> (readonly)
Returns cache of tools by name.
15 |
# File 'lib/mcp_client/client.rb', line 15 attr_reader :servers, :tool_cache, :logger |
Instance Method Details
#call_tool(tool_name, parameters) ⇒ Object
Calls a specific tool by name with the given parameters
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/mcp_client/client.rb', line 63 def call_tool(tool_name, parameters) tools = list_tools tool = tools.find { |t| t.name == tool_name } raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found" unless tool # Validate parameters against tool schema validate_params!(tool, parameters) # Find the server that owns this tool server = find_server_for_tool(tool) raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless server server.call_tool(tool_name, parameters) end |
#call_tool_streaming(tool_name, parameters) ⇒ Enumerator
Stream call of a specific tool by name with the given parameters. Returns an Enumerator yielding streaming updates if supported.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/mcp_client/client.rb', line 146 def call_tool_streaming(tool_name, parameters) tools = list_tools tool = tools.find { |t| t.name == tool_name } raise MCPClient::Errors::ToolNotFound, "Tool '#{tool_name}' not found" unless tool # Validate parameters against tool schema validate_params!(tool, parameters) # Find the server that owns this tool server = find_server_for_tool(tool) raise MCPClient::Errors::ServerNotFound, "No server found for tool '#{tool_name}'" unless server if server.respond_to?(:call_tool_streaming) server.call_tool_streaming(tool_name, parameters) else Enumerator.new do |yielder| yielder << server.call_tool(tool_name, parameters) end end end |
#call_tools(calls) ⇒ Array<Object>
Call multiple tools in batch
133 134 135 136 137 138 139 |
# File 'lib/mcp_client/client.rb', line 133 def call_tools(calls) calls.map do |call| name = call[:name] || call['name'] params = call[:parameters] || call['parameters'] || {} call_tool(name, params) end end |
#cleanup ⇒ Object
Clean up all server connections
98 99 100 |
# File 'lib/mcp_client/client.rb', line 98 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
104 105 106 |
# File 'lib/mcp_client/client.rb', line 104 def clear_cache @tool_cache.clear end |
#find_tool(pattern) ⇒ MCPClient::Tool?
Find the first tool whose name matches the given pattern
126 127 128 |
# File 'lib/mcp_client/client.rb', line 126 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)
118 119 120 121 |
# File 'lib/mcp_client/client.rb', line 118 def find_tools(pattern) rx = pattern.is_a?(Regexp) ? pattern : /#{Regexp.escape(pattern)}/ list_tools.select { |t| t.name.match(rx) } end |
#list_tools(cache: true) ⇒ Array<MCPClient::Tool>
Lists all available tools from all connected MCP servers
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/mcp_client/client.rb', line 45 def list_tools(cache: true) return @tool_cache.values if cache && !@tool_cache.empty? tools = [] servers.each do |server| server.list_tools.each do |tool| @tool_cache[tool.name] = tool tools << tool end end tools end |
#on_notification {|server, method, params| ... } ⇒ void
This method returns an undefined value.
Register a callback for JSON-RPC notifications from servers
111 112 113 |
# File 'lib/mcp_client/client.rb', line 111 def on_notification(&block) @notification_listeners << block end |
#ping(params = {}, server_index: nil) ⇒ Object
Ping the MCP server to check connectivity
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/mcp_client/client.rb', line 171 def ping(params = {}, 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(params) 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(params) end end |
#to_anthropic_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to Anthropic Claude tool specifications
91 92 93 94 95 |
# File 'lib/mcp_client/client.rb', line 91 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_openai_tools(tool_names: nil) ⇒ Array<Hash>
Convert MCP tools to OpenAI function specifications
82 83 84 85 86 |
# File 'lib/mcp_client/client.rb', line 82 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 |