Class: RubynCode::MCP::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/rubyn_code/mcp/client.rb

Overview

High-level MCP client that manages the connection lifecycle, tool discovery, and tool invocation for a single MCP server.

Defined Under Namespace

Classes: ClientError

Constant Summary collapse

INITIALIZE_TIMEOUT =
10

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, transport:) ⇒ Client

Returns a new instance of Client.

Parameters:

  • name (String)

    human-readable name for this MCP server connection

  • transport (StdioTransport, SSETransport)

    the underlying transport



19
20
21
22
23
24
# File 'lib/rubyn_code/mcp/client.rb', line 19

def initialize(name:, transport:)
  @name = name
  @transport = transport
  @tools_cache = nil
  @initialized = false
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



15
16
17
# File 'lib/rubyn_code/mcp/client.rb', line 15

def name
  @name
end

#transportObject (readonly)

Returns the value of attribute transport.



15
16
17
# File 'lib/rubyn_code/mcp/client.rb', line 15

def transport
  @transport
end

Class Method Details

.from_config(server_config) ⇒ Client

Factory method that creates a Client with the appropriate transport based on the server configuration.

Configs with a :url key use SSETransport; all others use StdioTransport.

Parameters:

  • server_config (Hash)

    configuration hash with :name, :command/:url, :args, :env

Returns:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/rubyn_code/mcp/client.rb', line 146

def from_config(server_config)
  name = server_config[:name]

  transport = if server_config[:url]
                SSETransport.new(
                  url: server_config[:url],
                  timeout: server_config[:timeout] || SSETransport::DEFAULT_TIMEOUT
                )
              else
                StdioTransport.new(
                  command: server_config[:command],
                  args: server_config[:args] || [],
                  env: server_config[:env] || {},
                  timeout: server_config[:timeout] || StdioTransport::DEFAULT_TIMEOUT
                )
              end

  new(name: name, transport: transport)
end

Instance Method Details

#call_tool(tool_name, arguments = {}) ⇒ Hash

Invokes a tool on the MCP server.

Parameters:

  • tool_name (String)

    the name of the tool to call

  • arguments (Hash) (defaults to: {})

    the arguments to pass to the tool

Returns:

  • (Hash)

    the tool’s result

Raises:



54
55
56
57
58
59
60
61
# File 'lib/rubyn_code/mcp/client.rb', line 54

def call_tool(tool_name, arguments = {})
  ensure_connected!

  @transport.send_request('tools/call', {
                            name: tool_name,
                            arguments: arguments
                          })
end

#connect!void

This method returns an undefined value.

Starts the transport, performs the MCP initialize handshake, and discovers available tools.

Raises:



31
32
33
34
35
36
37
38
# File 'lib/rubyn_code/mcp/client.rb', line 31

def connect!
  @transport.start!
  perform_initialize
  @initialized = true
rescue StandardError => e
  @transport.stop!
  raise ClientError, "Failed to connect to MCP server '#{@name}': #{e.message}"
end

#connected?Boolean

Returns whether the client is connected and the transport is alive.

Returns:

  • (Boolean)


134
135
136
# File 'lib/rubyn_code/mcp/client.rb', line 134

def connected?
  @initialized && @transport.alive?
end

#disconnect!void

This method returns an undefined value.

Gracefully disconnects from the MCP server.



123
124
125
126
127
128
129
# File 'lib/rubyn_code/mcp/client.rb', line 123

def disconnect!
  @transport.stop!
  @initialized = false
  @tools = nil
  @resources = nil
  @prompts = nil
end

#get_prompt(name, arguments = {}) ⇒ Hash

Fetches a prompt, expanding its template with the given arguments.

Parameters:

  • name (String)
  • arguments (Hash) (defaults to: {})

Returns:

  • (Hash)

    result with “messages” (and optionally “description”)



105
106
107
108
# File 'lib/rubyn_code/mcp/client.rb', line 105

def get_prompt(name, arguments = {})
  ensure_connected!
  @transport.send_request('prompts/get', { name: name, arguments: arguments })
end

#promptsArray<Hash>

Returns the prompts advertised by the server (empty unless the server declared the ‘prompts` capability). Each entry has “name” and optionally “description”/“arguments”.

Returns:

  • (Array<Hash>)


91
92
93
94
95
96
97
98
# File 'lib/rubyn_code/mcp/client.rb', line 91

def prompts
  return [] unless supports_prompts?

  @prompts ||= @transport.send_request('prompts/list')&.fetch('prompts', []) || []
rescue StandardError => e
  RubynCode::Debug.warn("MCP '#{@name}' prompts/list failed: #{e.message}")
  []
end

#read_resource(uri) ⇒ Hash

Reads a single resource by URI.

Parameters:

  • uri (String)

Returns:

  • (Hash)

    result with a “contents” array



81
82
83
84
# File 'lib/rubyn_code/mcp/client.rb', line 81

def read_resource(uri)
  ensure_connected!
  @transport.send_request('resources/read', { uri: uri })
end

#resourcesArray<Hash>

Returns the resources advertised by the server (empty unless the server declared the ‘resources` capability). Each entry is a Hash with “uri”, “name”, and optionally “description”/“mimeType”.

Returns:

  • (Array<Hash>)


68
69
70
71
72
73
74
75
# File 'lib/rubyn_code/mcp/client.rb', line 68

def resources
  return [] unless supports_resources?

  @resources ||= @transport.send_request('resources/list')&.fetch('resources', []) || []
rescue StandardError => e
  RubynCode::Debug.warn("MCP '#{@name}' resources/list failed: #{e.message}")
  []
end

#supports_prompts?Boolean

Returns whether the server advertised the prompts capability.

Returns:

  • (Boolean)

    whether the server advertised the prompts capability



116
117
118
# File 'lib/rubyn_code/mcp/client.rb', line 116

def supports_prompts?
  @server_capabilities.is_a?(Hash) && @server_capabilities.key?('prompts')
end

#supports_resources?Boolean

Returns whether the server advertised the resources capability.

Returns:

  • (Boolean)

    whether the server advertised the resources capability



111
112
113
# File 'lib/rubyn_code/mcp/client.rb', line 111

def supports_resources?
  @server_capabilities.is_a?(Hash) && @server_capabilities.key?('resources')
end

#toolsArray<Hash>

Returns the list of tool definitions from the MCP server. Each tool is a Hash with “name”, “description”, and “inputSchema” keys.

Returns:

  • (Array<Hash>)

    tool definitions in JSON Schema format



44
45
46
# File 'lib/rubyn_code/mcp/client.rb', line 44

def tools
  @tools ||= discover_tools
end