Class: MCP::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/mcp/client.rb,
lib/mcp/client/http.rb,
lib/mcp/client/tool.rb,
lib/mcp/client/stdio.rb,
lib/mcp/client/paginated_result.rb

Defined Under Namespace

Classes: HTTP, ListPromptsResult, ListResourceTemplatesResult, ListResourcesResult, ListToolsResult, RequestHandlerError, ServerError, SessionExpiredError, Stdio, Tool, ValidationError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transport:) ⇒ Client

Initializes a new MCP::Client instance.

Examples:

transport = MCP::Client::HTTP.new(url: "http://localhost:3000")
client = MCP::Client.new(transport: transport)

Parameters:

  • transport (Object)

    The transport object to use for communication with the server. The transport should be a duck type that responds to ‘send_request`. See the README for more details.



54
55
56
# File 'lib/mcp/client.rb', line 54

def initialize(transport:)
  @transport = transport
end

Instance Attribute Details

#transportObject (readonly)

The user may want to access additional transport-specific methods/attributes So keeping it public



60
61
62
# File 'lib/mcp/client.rb', line 60

def transport
  @transport
end

Instance Method Details

#call_tool(name: nil, tool: nil, arguments: nil, progress_token: nil) ⇒ Hash

Note:

The exact requirements for ‘arguments` are determined by the transport layer in use. Consult the documentation for your transport (e.g., MCP::Client::HTTP) for details.

Calls a tool via the transport layer and returns the full response from the server.

Examples:

Call by name

response = client.call_tool(name: "my_tool", arguments: { foo: "bar" })
content = response.dig("result", "content")

Call with a tool object

tool = client.tools.first
response = client.call_tool(tool: tool, arguments: { foo: "bar" })
structured_content = response.dig("result", "structuredContent")

Parameters:

  • name (String) (defaults to: nil)

    The name of the tool to call.

  • tool (MCP::Client::Tool) (defaults to: nil)

    The tool to be called.

  • arguments (Object, nil) (defaults to: nil)

    The arguments to pass to the tool.

  • progress_token (String, Integer, nil) (defaults to: nil)

    A token to request progress notifications from the server during tool execution.

Returns:

  • (Hash)

    The full JSON-RPC response from the transport.

Raises:

  • (ArgumentError)


273
274
275
276
277
278
279
280
281
282
283
# File 'lib/mcp/client.rb', line 273

def call_tool(name: nil, tool: nil, arguments: nil, progress_token: nil)
  tool_name = name || tool&.name
  raise ArgumentError, "Either `name:` or `tool:` must be provided." unless tool_name

  params = { name: tool_name, arguments: arguments }
  if progress_token
    params[:_meta] = { progressToken: progress_token }
  end

  request(method: "tools/call", params: params)
end

#complete(ref:, argument:, context: nil) ⇒ Hash

Requests completion suggestions from the server for a prompt argument or resource template URI.

Parameters:

  • ref (Hash)

    The reference, e.g. ‘{ type: “ref/prompt”, name: “my_prompt” }` or `{ type: “ref/resource”, uri: “file:///path” }`.

  • argument (Hash)

    The argument being completed, e.g. ‘{ name: “language”, value: “py” }`.

  • context (Hash, nil) (defaults to: nil)

    Optional context with previously resolved arguments.

Returns:

  • (Hash)

    The completion result with ‘“values”`, `“hasMore”`, and optionally `“total”`.



312
313
314
315
316
317
318
319
# File 'lib/mcp/client.rb', line 312

def complete(ref:, argument:, context: nil)
  params = { ref: ref, argument: argument }
  params[:context] = context if context

  response = request(method: "completion/complete", params: params)

  response.dig("result", "completion") || { "values" => [], "hasMore" => false }
end

#get_prompt(name:) ⇒ Hash

Gets a prompt from the server by name and returns its details.

Parameters:

  • name (String)

    The name of the prompt to get.

Returns:

  • (Hash)

    A hash containing the prompt details.



299
300
301
302
303
# File 'lib/mcp/client.rb', line 299

def get_prompt(name:)
  response = request(method: "prompts/get", params: { name: name })

  response.fetch("result", {})
end

#list_prompts(cursor: nil) ⇒ MCP::Client::ListPromptsResult

Returns a single page of prompts from the server.

Parameters:

  • cursor (String, nil) (defaults to: nil)

    Cursor from a previous page response.

Returns:



215
216
217
218
219
220
221
222
223
224
225
# File 'lib/mcp/client.rb', line 215

def list_prompts(cursor: nil)
  params = cursor ? { cursor: cursor } : nil
  response = request(method: "prompts/list", params: params)
  result = response["result"] || {}

  ListPromptsResult.new(
    prompts: result["prompts"] || [],
    next_cursor: result["nextCursor"],
    meta: result["_meta"],
  )
end

#list_resource_templates(cursor: nil) ⇒ MCP::Client::ListResourceTemplatesResult

Returns a single page of resource templates from the server.

Parameters:

  • cursor (String, nil) (defaults to: nil)

    Cursor from a previous page response.

Returns:



172
173
174
175
176
177
178
179
180
181
182
# File 'lib/mcp/client.rb', line 172

def list_resource_templates(cursor: nil)
  params = cursor ? { cursor: cursor } : nil
  response = request(method: "resources/templates/list", params: params)
  result = response["result"] || {}

  ListResourceTemplatesResult.new(
    resource_templates: result["resourceTemplates"] || [],
    next_cursor: result["nextCursor"],
    meta: result["_meta"],
  )
end

#list_resources(cursor: nil) ⇒ MCP::Client::ListResourcesResult

Returns a single page of resources from the server.

Parameters:

  • cursor (String, nil) (defaults to: nil)

    Cursor from a previous page response.

Returns:



129
130
131
132
133
134
135
136
137
138
139
# File 'lib/mcp/client.rb', line 129

def list_resources(cursor: nil)
  params = cursor ? { cursor: cursor } : nil
  response = request(method: "resources/list", params: params)
  result = response["result"] || {}

  ListResourcesResult.new(
    resources: result["resources"] || [],
    next_cursor: result["nextCursor"],
    meta: result["_meta"],
  )
end

#list_tools(cursor: nil) ⇒ MCP::Client::ListToolsResult

Returns a single page of tools from the server.

Examples:

Iterate all pages

cursor = nil
loop do
  page = client.list_tools(cursor: cursor)
  page.tools.each { |tool| puts tool.name }
  cursor = page.next_cursor
  break unless cursor
end

Parameters:

  • cursor (String, nil) (defaults to: nil)

    Cursor from a previous page response.

Returns:



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/mcp/client.rb', line 76

def list_tools(cursor: nil)
  params = cursor ? { cursor: cursor } : nil
  response = request(method: "tools/list", params: params)
  result = response["result"] || {}

  tools = (result["tools"] || []).map do |tool|
    Tool.new(
      name: tool["name"],
      description: tool["description"],
      input_schema: tool["inputSchema"],
    )
  end

  ListToolsResult.new(tools: tools, next_cursor: result["nextCursor"], meta: result["_meta"])
end

#pingHash

Sends a ‘ping` request to the server to verify the connection is alive. Per the MCP spec, the server responds with an empty result.

Examples:

client.ping # => {}

Returns:

  • (Hash)

    An empty hash on success.

Raises:

  • (ServerError)

    If the server returns a JSON-RPC error.

  • (ValidationError)

    If the response ‘result` is missing or not a Hash.

See Also:



332
333
334
335
336
337
# File 'lib/mcp/client.rb', line 332

def ping
  result = request(method: Methods::PING)["result"]
  raise ValidationError, "Response validation failed: missing or invalid `result`" unless result.is_a?(Hash)

  result
end

#promptsArray<Hash>

Returns every prompt available on the server. Iterates through all pages automatically when the server paginates, so the full collection is returned regardless of the server’s ‘page_size` setting. Use #list_prompts when you need fine-grained cursor control.

Each call will make a new request - the result is not cached.

Returns:

  • (Array<Hash>)

    An array of available prompts.



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/mcp/client.rb', line 234

def prompts
  # TODO: consider renaming to `list_all_prompts`.
  all_prompts = []
  seen = Set.new
  cursor = nil

  loop do
    page = list_prompts(cursor: cursor)
    all_prompts.concat(page.prompts)
    next_cursor = page.next_cursor
    break if next_cursor.nil? || seen.include?(next_cursor)

    seen << next_cursor
    cursor = next_cursor
  end

  all_prompts
end

#read_resource(uri:) ⇒ Array<Hash>

Reads a resource from the server by URI and returns the contents.

Parameters:

  • uri (String)

    The URI of the resource to read.

Returns:

  • (Array<Hash>)

    An array of resource contents (text or blob).



289
290
291
292
293
# File 'lib/mcp/client.rb', line 289

def read_resource(uri:)
  response = request(method: "resources/read", params: { uri: uri })

  response.dig("result", "contents") || []
end

#resource_templatesArray<Hash>

Returns every resource template available on the server. Iterates through all pages automatically when the server paginates, so the full collection is returned regardless of the server’s ‘page_size` setting. Use #list_resource_templates when you need fine-grained cursor control.

Each call will make a new request - the result is not cached.

Returns:

  • (Array<Hash>)

    An array of available resource templates.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/mcp/client.rb', line 191

def resource_templates
  # TODO: consider renaming to `list_all_resource_templates`.
  all_templates = []
  seen = Set.new
  cursor = nil

  loop do
    page = list_resource_templates(cursor: cursor)
    all_templates.concat(page.resource_templates)
    next_cursor = page.next_cursor
    break if next_cursor.nil? || seen.include?(next_cursor)

    seen << next_cursor
    cursor = next_cursor
  end

  all_templates
end

#resourcesArray<Hash>

Returns every resource available on the server. Iterates through all pages automatically when the server paginates, so the full collection is returned regardless of the server’s ‘page_size` setting. Use #list_resources when you need fine-grained cursor control.

Each call will make a new request - the result is not cached.

Returns:

  • (Array<Hash>)

    An array of available resources.



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

def resources
  # TODO: consider renaming to `list_all_resources`.
  all_resources = []
  seen = Set.new
  cursor = nil

  loop do
    page = list_resources(cursor: cursor)
    all_resources.concat(page.resources)
    next_cursor = page.next_cursor
    break if next_cursor.nil? || seen.include?(next_cursor)

    seen << next_cursor
    cursor = next_cursor
  end

  all_resources
end

#toolsArray<MCP::Client::Tool>

Returns every tool available on the server. Iterates through all pages automatically when the server paginates, so the full collection is returned regardless of the server’s ‘page_size` setting. Use #list_tools when you need fine-grained cursor control.

Each call will make a new request - the result is not cached.

Examples:

tools = client.tools
tools.each do |tool|
  puts tool.name
end

Returns:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/mcp/client.rb', line 105

def tools
  # TODO: consider renaming to `list_all_tools`.
  all_tools = []
  seen = Set.new
  cursor = nil

  loop do
    page = list_tools(cursor: cursor)
    all_tools.concat(page.tools)
    next_cursor = page.next_cursor
    break if next_cursor.nil? || seen.include?(next_cursor)

    seen << next_cursor
    cursor = next_cursor
  end

  all_tools
end