class RubyLLM::MCP::Client
Speaks JSON-RPC to one MCP server over a transport. It speaks the 2026-07-28 revision and falls back to the initialize handshake for servers that predate it, declaring only its extensions so those servers never call back. A server that rejects 2026-07-28 while listing it gets the request once more; one that rejects it while listing a revision from LEGACY_VERSIONS, or without listing any, gets the handshake instead; one that answers the handshake with a revision missing from LEGACY_VERSIONS is disconnected.
Servers that predate 2026-07-28 may announce that their tools, prompts, or resources changed on the stream of any request. Those notifications reach the block given to ::new once the request is answered, so the block can make requests of its own. #listen opens a subscription for them instead: subscriptions/listen on newer servers, resources/subscribe and the session’s own stream on older ones, which never announce the status of tasks. Their subscription counts as acknowledged once a ping confirms the session, starting a new one if it ended.
An MCP builds its own client. Applications use one only to read meta, when they send a server requests of their own.
Public Instance Methods
Source
# File lib/ruby_llm/mcp/client.rb, line 123 def meta { 'io.modelcontextprotocol/protocolVersion' => VERSION, 'io.modelcontextprotocol/clientInfo' => client_info, 'io.modelcontextprotocol/clientCapabilities' => @capabilities } end
Returns the _meta the client adds to every request it sends a server on the 2026-07-28 revision: the revision, the client’s name and version, and its capabilities. Merge it into requests you send the server yourself, so they describe the client the same way:
meta = RubyLLM::MCP::Client.new(transport).meta # => { "io.modelcontextprotocol/protocolVersion" => "2026-07-28", # "io.modelcontextprotocol/clientInfo" => { name: "ruby_llm", version: "2.2.0" }, # "io.modelcontextprotocol/clientCapabilities" => {} } params = { name: "roots", arguments: {}, _meta: meta }
Servers on an older revision get none of these fields: they learn the same from the initialize handshake.