Class: LLM::MCP
- Inherits:
-
Object
- Object
- LLM::MCP
- Includes:
- RPC
- Defined in:
- lib/llm/mcp.rb,
lib/llm/mcp/rpc.rb,
lib/llm/mcp/error.rb,
lib/llm/mcp/router.rb,
lib/llm/mcp/command.rb,
lib/llm/mcp/mailbox.rb
Overview
The LLM::MCP class provides access to servers that implement the Model Context Protocol. MCP defines a standard way for clients and servers to exchange capabilities such as tools, prompts, resources, and other structured interactions.
In llm.rb, LLM::MCP currently supports stdio and HTTP transports and focuses on discovering tools that can be used through LLM::Context and LLM::Agent.
An MCP client is stateful. Coordinate lifecycle operations such as #start and #stop; request methods can be issued concurrently and responses are matched by JSON-RPC id.
Defined Under Namespace
Modules: RPC, Transport Classes: Command, Mailbox, Router
Constant Summary collapse
- Error =
Class.new(LLM::Error) do attr_reader :code, :data ## # @param [Hash] response # The full response from the MCP process, including the error object # @return [LLM::MCP::Error] def self.from(response:) error = response.fetch("error") new(*error.values_at("message", "code", "data")) end ## # @param [String] message # The error message # @param [Integer] code # The error code # @param [Object] data # Additional error data provided by the MCP process def initialize(, code = nil, data = nil) super() @code = code @data = data end end
- MismatchError =
Class.new(Error) do ## # @return [Integer, String] # The request id the client was waiting for attr_reader :expected_id ## # @return [Integer, String] # The response id received from the server attr_reader :actual_id ## # @param [Integer, String] expected_id # The request id the client was waiting for # @param [Integer, String] actual_id # The response id received from the server instead def initialize(expected_id:, actual_id:) @expected_id = expected_id @actual_id = actual_id super() end ## # @return [String] def "mismatched MCP response id #{actual_id.inspect} " \ "while waiting for #{expected_id.inspect}" end end
- TimeoutError =
Class.new(Error)
Class Method Summary collapse
- .clients ⇒ Object private
-
.http(llm = nil, **http) ⇒ LLM::MCP
Builds an MCP client that uses the HTTP transport.
-
.stdio(llm = nil, **stdio) ⇒ LLM::MCP
Builds an MCP client that uses the stdio transport.
Instance Method Summary collapse
-
#call_tool(name, arguments = {}) ⇒ Object
Calls a tool by name with the given arguments.
-
#find_prompt(name:, arguments: nil) ⇒ LLM::Object
(also: #get_prompt)
Returns a prompt by name.
-
#initialize(llm = nil, stdio: nil, http: nil, timeout: 30) ⇒ LLM::MCP
constructor
A new MCP instance.
-
#persist! ⇒ LLM::MCP
(also: #persistent)
Configures an HTTP MCP transport to use a persistent connection pool via the optional dependency [Net::HTTP::Persistent](github.com/drbrain/net-http-persistent).
-
#prompts ⇒ Array<LLM::Object>
Returns the prompts provided by the MCP process.
-
#run { ... } ⇒ void
Starts the MCP client for the duration of a block and then stops it.
-
#start ⇒ void
Starts the MCP process.
-
#stop ⇒ void
Stops the MCP process.
-
#tools ⇒ Array<Class<LLM::Tool>>
Returns the tools provided by the MCP process.
Methods included from RPC
Constructor Details
#initialize(llm = nil, stdio: nil, http: nil, timeout: 30) ⇒ LLM::MCP
Returns A new MCP instance.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/llm/mcp.rb', line 75 def initialize(llm = nil, stdio: nil, http: nil, timeout: 30) @llm = llm @timeout = timeout if stdio && http raise ArgumentError, "stdio and http are mutually exclusive" elsif stdio @command = Command.new(**stdio) @transport = Transport::Stdio.new(command:) elsif http persistent = http.delete(:persistent) @transport = Transport::HTTP.new(**http, timeout:) @transport.persistent if persistent else raise ArgumentError, "stdio or http is required" end end |
Class Method Details
.clients ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
31 32 33 |
# File 'lib/llm/mcp.rb', line 31 def self.clients @clients end |
.http(llm = nil, **http) ⇒ LLM::MCP
Builds an MCP client that uses the HTTP transport.
53 54 55 |
# File 'lib/llm/mcp.rb', line 53 def self.http(llm = nil, **http) new(llm, http:) end |
.stdio(llm = nil, **stdio) ⇒ LLM::MCP
Builds an MCP client that uses the stdio transport.
42 43 44 |
# File 'lib/llm/mcp.rb', line 42 def self.stdio(llm = nil, **stdio) new(llm, stdio:) end |
Instance Method Details
#call_tool(name, arguments = {}) ⇒ Object
Calls a tool by name with the given arguments
178 179 180 181 |
# File 'lib/llm/mcp.rb', line 178 def call_tool(name, arguments = {}) res = call(transport, "tools/call", {name:, arguments:}) adapt_tool_result(res) end |
#find_prompt(name:, arguments: nil) ⇒ LLM::Object Also known as: get_prompt
Returns a prompt by name.
158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/llm/mcp.rb', line 158 def find_prompt(name:, arguments: nil) params = {name:} params[:arguments] = arguments if arguments res = call(transport, "prompts/get", params) res["messages"] = [*res["messages"]].map do || LLM::Message.new( ["role"], adapt_content(["content"]), {original_content: ["content"]} ) end LLM::Object.from(res) end |
#persist! ⇒ LLM::MCP Also known as: persistent
Configures an HTTP MCP transport to use a persistent connection pool via the optional dependency [Net::HTTP::Persistent](github.com/drbrain/net-http-persistent)
131 132 133 134 |
# File 'lib/llm/mcp.rb', line 131 def persist! transport.persist! self end |
#prompts ⇒ Array<LLM::Object>
Returns the prompts provided by the MCP process.
148 149 150 151 |
# File 'lib/llm/mcp.rb', line 148 def prompts res = call(transport, "prompts/list") LLM::Object.from(res["prompts"]) end |
#run { ... } ⇒ void
This method returns an undefined value.
Starts the MCP client for the duration of a block and then stops it.
117 118 119 120 121 122 |
# File 'lib/llm/mcp.rb', line 117 def run start yield ensure stop end |
#start ⇒ void
This method returns an undefined value.
Starts the MCP process.
95 96 97 98 99 |
# File 'lib/llm/mcp.rb', line 95 def start transport.start call(transport, "initialize", {clientInfo: {name: "llm.rb", version: LLM::VERSION}}) call(transport, "notifications/initialized") end |
#stop ⇒ void
This method returns an undefined value.
Stops the MCP process.
104 105 106 107 |
# File 'lib/llm/mcp.rb', line 104 def stop transport.stop nil end |