Class: Mistri::MCP::Client
- Inherits:
-
Object
- Object
- Mistri::MCP::Client
- Defined in:
- lib/mistri/mcp/client.rb
Overview
A Model Context Protocol client: the initialize handshake, tools/list with pagination, and tools/call, over one of two wires. url: speaks Streamable HTTP on the same persistent transport the providers use; command: spawns a local stdio server with credentials in its environment.
Mistri::MCP::Client.new(url: "https://mcp.linear.app/mcp",
token: -> { connection.bearer_token })
Mistri::MCP::Client.new(command: ["npx", "-y", "some-mcp-server"],
env: { "API_KEY" => key })
HTTP auth is a headers hash or token: a string or a callable. A callable resolves per request, and a 401 retries once after re-resolving, so a host's refresh logic lives in one lambda. A session the server expires (404 with a session attached) transparently re-initializes, per spec. Remote URLs default to public HTTPS. allow_non_public: is consulted only for otherwise blocked addresses and plain HTTP remains loopback-only. max_record_bytes bounds one JSON body, SSE line, or stdio record without imposing a lifetime limit on an SSE stream.
One client serializes its calls; parallel tool calls against one server queue rather than interleave.
Constant Summary collapse
- PROTOCOL_VERSION =
"2025-11-25"- SUPPORTED_VERSIONS =
%w[2025-11-25 2025-06-18 2025-03-26 2024-11-05].freeze
Instance Attribute Summary collapse
-
#server_info ⇒ Object
readonly
Returns the value of attribute server_info.
Instance Method Summary collapse
- #call_tool(name, arguments = {}) ⇒ Object
- #close ⇒ Object
- #connect ⇒ Object
-
#initialize(url: nil, command: nil, env: {}, token: nil, headers: {}, client_name: "mistri", open_timeout: 15, read_timeout: 120, allow_non_public: nil, max_tool_pages: 100, max_tools: 10_000, max_record_bytes: DEFAULT_MAX_RECORD_BYTES) ⇒ Client
constructor
A new instance of Client.
-
#tools(refresh: false) ⇒ Object
The server's tools as it describes them: hashes with "name", "description", and "inputSchema".
Constructor Details
#initialize(url: nil, command: nil, env: {}, token: nil, headers: {}, client_name: "mistri", open_timeout: 15, read_timeout: 120, allow_non_public: nil, max_tool_pages: 100, max_tools: 10_000, max_record_bytes: DEFAULT_MAX_RECORD_BYTES) ⇒ Client
Returns a new instance of Client.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/mistri/mcp/client.rb', line 33 def initialize(url: nil, command: nil, env: {}, token: nil, headers: {}, client_name: "mistri", open_timeout: 15, read_timeout: 120, allow_non_public: nil, max_tool_pages: 100, max_tools: 10_000, max_record_bytes: DEFAULT_MAX_RECORD_BYTES) if [url, command].compact.length != 1 raise ConfigurationError, "pass exactly one of url: or command:" end validate_limit(:max_tool_pages, max_tool_pages) validate_limit(:max_tools, max_tools) validate_limit(:max_record_bytes, max_record_bytes) @wire = if url Wires::Http.new(url: url, token: token, headers: headers, open_timeout: open_timeout, read_timeout: read_timeout, allow_non_public: allow_non_public, max_record_bytes: max_record_bytes) else Wires::Stdio.new(command: command, env: env, read_timeout: read_timeout, max_record_bytes: max_record_bytes) end @client_name = client_name @mutex = Mutex.new @serial = 0 @connected = false @max_tool_pages = max_tool_pages @max_tools = max_tools end |
Instance Attribute Details
#server_info ⇒ Object (readonly)
Returns the value of attribute server_info.
31 32 33 |
# File 'lib/mistri/mcp/client.rb', line 31 def server_info @server_info end |
Instance Method Details
#call_tool(name, arguments = {}) ⇒ Object
71 72 73 |
# File 'lib/mistri/mcp/client.rb', line 71 def call_tool(name, arguments = {}) @mutex.synchronize { request("tools/call", { name: name, arguments: arguments }) } end |
#close ⇒ Object
80 81 82 83 |
# File 'lib/mistri/mcp/client.rb', line 80 def close @mutex.synchronize { reset_wire } nil end |
#connect ⇒ Object
75 76 77 78 |
# File 'lib/mistri/mcp/client.rb', line 75 def connect @mutex.synchronize { ensure_connected } self end |
#tools(refresh: false) ⇒ Object
The server's tools as it describes them: hashes with "name", "description", and "inputSchema". Cached; refresh: true re-lists.
64 65 66 67 68 69 |
# File 'lib/mistri/mcp/client.rb', line 64 def tools(refresh: false) @mutex.synchronize do @tools = nil if refresh @tools ||= list_tools end end |