Class: SolidLoop::McpClient
- Inherits:
-
Object
- Object
- SolidLoop::McpClient
- Defined in:
- lib/solid_loop/mcp_client.rb
Overview
Thin MCP protocol layer: builds JSON-RPC envelopes and delegates I/O to a transport implementing SolidLoop::Mcp::Transport. Defaults to Mcp::HttpTransport built from the url/token/headers keywords.
Instance Attribute Summary collapse
-
#last_result ⇒ Object
readonly
Returns the value of attribute last_result.
-
#transport ⇒ Object
readonly
Returns the value of attribute transport.
Instance Method Summary collapse
-
#call_tool(session_id, name, arguments, meta: nil) ⇒ Object
meta: optional MCP
_metahash for the tools/call params (e.g. the per-ToolCall idempotency key under "solidloop/idempotencyKey"). -
#get_prompt(session_id, name, arguments = {}) ⇒ Object
Returns the MCP prompt result: { "description" => ..., "messages" => [...] }.
-
#initialize(url = nil, token: nil, timeout: 60, headers: {}, initialize_params: {}, stateless: false, transport: nil, context: nil) ⇒ McpClient
constructor
A new instance of McpClient.
- #initialize_session ⇒ Object
- #last_tool_error? ⇒ Boolean
- #list_prompts(session_id) ⇒ Object
- #list_resources(session_id) ⇒ Object
- #list_tools(session_id) ⇒ Object
-
#read_resource(session_id, uri) ⇒ Object
Returns the MCP resource contents array (text and/or blob entries).
-
#server_capabilities ⇒ Object
Capabilities advertised by the server in the last initialize response.
- #wire_log ⇒ Object
Constructor Details
#initialize(url = nil, token: nil, timeout: 60, headers: {}, initialize_params: {}, stateless: false, transport: nil, context: nil) ⇒ McpClient
Returns a new instance of McpClient.
14 15 16 17 18 19 20 |
# File 'lib/solid_loop/mcp_client.rb', line 14 def initialize(url = nil, token: nil, timeout: 60, headers: {}, initialize_params: {}, stateless: false, transport: nil, context: nil) @initialize_params = initialize_params @stateless = stateless @transport = transport || Mcp::HttpTransport.new(url: url, token: token, timeout: timeout, headers: headers) @context = context @last_result = nil end |
Instance Attribute Details
#last_result ⇒ Object (readonly)
Returns the value of attribute last_result.
12 13 14 |
# File 'lib/solid_loop/mcp_client.rb', line 12 def last_result @last_result end |
#transport ⇒ Object (readonly)
Returns the value of attribute transport.
12 13 14 |
# File 'lib/solid_loop/mcp_client.rb', line 12 def transport @transport end |
Instance Method Details
#call_tool(session_id, name, arguments, meta: nil) ⇒ Object
meta: optional MCP _meta hash for the tools/call params (e.g. the
per-ToolCall idempotency key under "solidloop/idempotencyKey").
62 63 64 65 66 67 68 69 70 |
# File 'lib/solid_loop/mcp_client.rb', line 62 def call_tool(session_id, name, arguments, meta: nil) params = { name: name, arguments: arguments } params[:_meta] = if .present? deliver(envelope("tools/call", params), session_id: session_id) validate_result!(session_id) process_tool_result(last_result.body) end |
#get_prompt(session_id, name, arguments = {}) ⇒ Object
Returns the MCP prompt result: { "description" => ..., "messages" => [...] }.
90 91 92 93 94 95 |
# File 'lib/solid_loop/mcp_client.rb', line 90 def get_prompt(session_id, name, arguments = {}) deliver(envelope("prompts/get", { name: name, arguments: arguments }), session_id: session_id) validate_result!(session_id) body_hash["result"] || {} end |
#initialize_session ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/solid_loop/mcp_client.rb', line 34 def initialize_session base_params = { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "solid_loop", version: SolidLoop::VERSION } } deliver(envelope("initialize", base_params.deep_merge(@initialize_params))) unless success? raise McpError, "Failed to initialize MCP session: #{last_result.status} #{body_string}" end if (error = protocol_error) raise McpError, "Failed to initialize MCP session: #{error.to_json}" end last_result.session_id || stateless_session_id end |
#last_tool_error? ⇒ Boolean
78 79 80 |
# File 'lib/solid_loop/mcp_client.rb', line 78 def last_tool_error? body_hash.dig("result", "isError") == true end |
#list_prompts(session_id) ⇒ Object
82 83 84 85 86 87 |
# File 'lib/solid_loop/mcp_client.rb', line 82 def list_prompts(session_id) deliver(envelope("prompts/list", {}), session_id: session_id) validate_result!(session_id) body_hash.dig("result", "prompts") || [] end |
#list_resources(session_id) ⇒ Object
97 98 99 100 101 102 |
# File 'lib/solid_loop/mcp_client.rb', line 97 def list_resources(session_id) deliver(envelope("resources/list", {}), session_id: session_id) validate_result!(session_id) body_hash.dig("result", "resources") || [] end |
#list_tools(session_id) ⇒ Object
53 54 55 56 57 58 |
# File 'lib/solid_loop/mcp_client.rb', line 53 def list_tools(session_id) deliver(envelope("tools/list", {}), session_id: session_id) validate_result!(session_id) body_hash.dig("result", "tools") || [] end |
#read_resource(session_id, uri) ⇒ Object
Returns the MCP resource contents array (text and/or blob entries).
105 106 107 108 109 110 |
# File 'lib/solid_loop/mcp_client.rb', line 105 def read_resource(session_id, uri) deliver(envelope("resources/read", { uri: uri }), session_id: session_id) validate_result!(session_id) body_hash.dig("result", "contents") || [] end |
#server_capabilities ⇒ Object
Capabilities advertised by the server in the last initialize response. Meaningful right after initialize_session.
74 75 76 |
# File 'lib/solid_loop/mcp_client.rb', line 74 def server_capabilities body_hash.dig("result", "capabilities") || {} end |
#wire_log ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/solid_loop/mcp_client.rb', line 22 def wire_log return "No response captured" unless last_result lines = [ "request: #{pretty_json(last_result.raw_request)}", "status: #{last_result.status}", "response: #{pretty_json(last_result.raw_response)}" ] lines << "stderr: #{last_result.stderr}" if last_result.stderr.present? lines.join("\n") end |