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/oauth.rb,
lib/mcp/client/stdio.rb,
lib/mcp/client/oauth/flow.rb,
lib/mcp/client/oauth/pkce.rb,
lib/mcp/client/oauth/provider.rb,
lib/mcp/client/oauth/discovery.rb,
lib/mcp/client/paginated_result.rb,
lib/mcp/client/oauth/in_memory_storage.rb,
lib/mcp/client/oauth/storage_backed_provider.rb,
lib/mcp/client/oauth/client_credentials_provider.rb

Defined Under Namespace

Modules: OAuth 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.



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

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



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

def transport
  @transport
end

Instance Method Details

#call_tool(name: nil, tool: nil, arguments: nil, progress_token: nil, meta: nil, cancellation: 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")

Cancellable call

cancellation = MCP::Cancellation.new
Thread.new do
  client.call_tool(name: "slow_tool", arguments: {}, cancellation: cancellation)
rescue MCP::CancelledError
  # cleanup
end
cancellation.cancel(reason: "user pressed cancel")

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.

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

    Additional ‘_meta` entries to send with the request, e.g. the W3C Trace Context keys reserved by SEP-414 (`MCP::TraceContext::TRACEPARENT_META_KEY`, `tracestate`, `baggage`). `progress_token` takes precedence over a `progressToken` entry in `meta`.

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token. Cancelling it from another thread sends ‘notifications/cancelled` to the server and raises `MCP::CancelledError` from this call.

Returns:

  • (Hash)

    The full JSON-RPC response from the transport.

Raises:

  • (ArgumentError)


295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/mcp/client.rb', line 295

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

  params = { name: tool_name, arguments: arguments }
  meta_entries = meta ? meta.dup : {}
  if progress_token
    meta_entries.delete("progressToken")
    meta_entries[:progressToken] = progress_token
  end
  params[:_meta] = meta_entries unless meta_entries.empty?

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

#complete(ref:, argument:, context: nil, meta: nil, cancellation: 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.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

Returns:

  • (Hash)

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



346
347
348
349
350
351
352
353
# File 'lib/mcp/client.rb', line 346

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

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

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

#connect(client_info: nil, protocol_version: nil, capabilities: {}) ⇒ Hash?

Performs the MCP ‘initialize` handshake by delegating to the transport (e.g. `MCP::Client::HTTP`, `MCP::Client::Stdio`). Returns the server’s ‘InitializeResult`.

When the transport does not respond to ‘:connect`, this is a no-op and returns `nil`.

modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#initialization

Parameters:

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

    ‘{ name:, version: }` identifying the client.

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

    Protocol version to offer.

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

    Capabilities advertised by the client. May include an ‘extensions` member per SEP-2133, keyed by reverse-DNS extension identifiers, e.g. `{ extensions: { “com.example/feature” => {} } }`.

Returns:

  • (Hash, nil)

    The server’s ‘InitializeResult`, or `nil` when the transport does not expose an explicit handshake.



86
87
88
89
90
91
92
93
94
# File 'lib/mcp/client.rb', line 86

def connect(client_info: nil, protocol_version: nil, capabilities: {})
  return unless transport.respond_to?(:connect)

  transport.connect(
    client_info: client_info,
    protocol_version: protocol_version,
    capabilities: capabilities,
  )
end

#connected?Boolean

Returns true once ‘connect` has completed the handshake on the underlying transport. Transports that do not expose connection state are assumed connected and return `true`.

Returns:

  • (Boolean)


99
100
101
102
103
# File 'lib/mcp/client.rb', line 99

def connected?
  return transport.connected? if transport.respond_to?(:connected?)

  true
end

#get_prompt(name:, meta: nil, cancellation: nil) ⇒ Hash

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

Parameters:

  • name (String)

    The name of the prompt to get.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

Returns:

  • (Hash)

    A hash containing the prompt details.



330
331
332
333
334
# File 'lib/mcp/client.rb', line 330

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

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

#list_prompts(cursor: nil, meta: nil, cancellation: 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.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

Returns:



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/mcp/client.rb', line 235

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

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

#list_resource_templates(cursor: nil, meta: nil, cancellation: 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.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

Returns:



202
203
204
205
206
207
208
209
210
211
212
# File 'lib/mcp/client.rb', line 202

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

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

#list_resources(cursor: nil, meta: nil, cancellation: 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.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

Returns:



169
170
171
172
173
174
175
176
177
178
179
# File 'lib/mcp/client.rb', line 169

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

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

#list_tools(cursor: nil, meta: nil, cancellation: 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.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional token; cancelling it sends ‘notifications/cancelled` to the server and raises `MCP::CancelledError` from this call.

Returns:



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/mcp/client.rb', line 123

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

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

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

#ping(meta: nil, cancellation: nil) ⇒ Hash

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 # => {}

Parameters:

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

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:



369
370
371
372
373
374
# File 'lib/mcp/client.rb', line 369

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

  result
end

#prompts(cancellation: nil) ⇒ Array<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.

Parameters:

Returns:

  • (Array<Hash>)

    An array of available prompts.



255
256
257
258
# File 'lib/mcp/client.rb', line 255

def prompts(cancellation: nil)
  # TODO: consider renaming to `list_all_prompts`.
  fetch_all_pages { |cursor| list_prompts(cursor: cursor, cancellation: cancellation) }.flat_map(&:prompts)
end

#read_resource(uri:, meta: nil, cancellation: nil) ⇒ Array<Hash>

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

Parameters:

  • uri (String)

    The URI of the resource to read.

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

    Additional ‘_meta` entries to send with the request, e.g. SEP-414 trace context (see TraceContext).

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token.

Returns:

  • (Array<Hash>)

    An array of resource contents (text or blob).



317
318
319
320
321
# File 'lib/mcp/client.rb', line 317

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

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

#resource_templates(cancellation: nil) ⇒ Array<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.

Parameters:

Returns:

  • (Array<Hash>)

    An array of available resource templates.



222
223
224
225
# File 'lib/mcp/client.rb', line 222

def resource_templates(cancellation: nil)
  # TODO: consider renaming to `list_all_resource_templates`.
  fetch_all_pages { |cursor| list_resource_templates(cursor: cursor, cancellation: cancellation) }.flat_map(&:resource_templates)
end

#resources(cancellation: nil) ⇒ Array<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.

Parameters:

Returns:

  • (Array<Hash>)

    An array of available resources.



189
190
191
192
# File 'lib/mcp/client.rb', line 189

def resources(cancellation: nil)
  # TODO: consider renaming to `list_all_resources`.
  fetch_all_pages { |cursor| list_resources(cursor: cursor, cancellation: cancellation) }.flat_map(&:resources)
end

#server_infoObject

The server’s ‘InitializeResult` (protocol version, capabilities, server info, instructions), as reported by the transport after a successful `connect`. Returns `nil` before `connect`, after `close`, or when the transport does not expose a cached handshake result.



67
68
69
# File 'lib/mcp/client.rb', line 67

def server_info
  transport.server_info if transport.respond_to?(:server_info)
end

#tools(cancellation: nil) ⇒ Array<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

Parameters:

  • cancellation (MCP::Cancellation, nil) (defaults to: nil)

    Optional cancellation token. Cancelling it aborts whichever page is currently in flight; pages already returned are kept, but the call raises ‘MCP::CancelledError` instead of returning the partial set.

Returns:



156
157
158
159
# File 'lib/mcp/client.rb', line 156

def tools(cancellation: nil)
  # TODO: consider renaming to `list_all_tools`.
  fetch_all_pages { |cursor| list_tools(cursor: cursor, cancellation: cancellation) }.flat_map(&:tools)
end