Class: LittleGhost::MCP::Client
- Inherits:
-
Object
- Object
- LittleGhost::MCP::Client
- Defined in:
- lib/little_ghost/mcp/client.rb
Overview
Client is the lower-level interface for loading tools from an MCP server. Most Agents can declare a reusable Toolset instead. Use Client directly when an application needs a custom transport.
transport = LittleGhost::MCP::HTTPTransport.new(url: "https://mcp.example/rpc")
client = LittleGhost::MCP::Client.new(transport:)
client.tools.map(&:tool_name) # => ["search", "fetch"]
Tool names are normalized and checked for collisions. LittleGhost limits
catalog size, schema complexity, and returned media before creating Tool
classes. tool_mapper and result_mapper use the same Definition,
Result, and Call values as Toolset.
The server chooses its definitions and results. LittleGhost checks their structure before creating Tools, but the application still chooses which servers and operations an Agent may use.
A client and its transport represent one authenticated server session. Create a separate pair for each authenticated user or service identity. Protocol initialization and subsequent requests through one client are serialized; do not share its transport with another client.
Defined Under Namespace
Classes: PreparedDefinition
Constant Summary collapse
- MAX_TOOL_NAME_LENGTH =
:nodoc:
64- ALIAS_DIGEST_LENGTH =
:nodoc:
12- DEFAULT_MAX_TOOLS =
:nodoc:
1_000- DEFAULT_MAX_PAGES =
:nodoc:
100- DEFAULT_MAX_DISCOVERY_BYTES =
:nodoc:
10 * 1024 * 1024
- DEFAULT_MAX_DISCOVERY_NODES =
:nodoc:
100_000- DEFAULT_MAX_CURSOR_BYTES =
:nodoc:
16 * 1024
- DEFAULT_MAX_DEFINITION_DEPTH =
:nodoc:
64- DEFAULT_MAX_DEFINITION_NODES =
:nodoc:
10_000- DEFAULT_MAX_SCHEMA_PATTERNS =
:nodoc:
1_000- DEFAULT_MAX_SCHEMA_PATTERN_BYTES =
:nodoc:
1024 * 1024
- DEFAULT_MAX_SCHEMA_PATTERN_SOURCE_BYTES =
:nodoc:
64 * 1024
- DEFAULT_MAX_IMAGES =
:nodoc:
20- DEFAULT_MAX_IMAGE_BYTES =
:nodoc:
16 * 1024 * 1024
- DEFAULT_MAX_TOTAL_IMAGE_BYTES =
:nodoc:
64 * 1024 * 1024
- ECMA_REGEXP_RESOLVER =
:nodoc:
lambda do |pattern| # :nodoc: source = JSONSchemer::EcmaRegexp.ruby_equivalent(pattern) Regexp.new(source, timeout: Tool::SchemaValidator::REGEXP_TIMEOUT) end
Instance Method Summary collapse
-
#call(name, arguments, context: nil, binding: Tool::Binding.new) ⇒ Object
Calls a generated or named Tool and returns the common Tool execution result.
-
#initialize(transport:, name: "mcp", tool_mapper: nil, result_mapper: nil) ⇒ Client
constructor
Uses a transport that responds to
send. -
#tools(context: nil, binding: Tool::Binding.new) ⇒ Object
Negotiates the protocol when needed, then provides Tool classes for the server's current definitions.
Constructor Details
#initialize(transport:, name: "mcp", tool_mapper: nil, result_mapper: nil) ⇒ Client
Uses a transport that responds to send. tool_mapper receives each
generated Tool class and may return a configured class or nil.
result_mapper receives Result and Call values. Catalog, schema, and
media limits use fixed framework defaults.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/little_ghost/mcp/client.rb', line 195 def initialize( transport:, name: "mcp", tool_mapper: nil, result_mapper: nil ) validate_callback(tool_mapper, :tool_mapper) validate_callback(result_mapper, :result_mapper) @transport = transport @name = String(name) @tool_mapper = tool_mapper @result_mapper = result_mapper @max_tools = DEFAULT_MAX_TOOLS @max_pages = DEFAULT_MAX_PAGES @max_discovery_bytes = DEFAULT_MAX_DISCOVERY_BYTES @max_discovery_nodes = DEFAULT_MAX_DISCOVERY_NODES @max_definition_depth = DEFAULT_MAX_DEFINITION_DEPTH @max_definition_nodes = DEFAULT_MAX_DEFINITION_NODES @max_images = DEFAULT_MAX_IMAGES @max_image_bytes = DEFAULT_MAX_IMAGE_BYTES @max_total_image_bytes = DEFAULT_MAX_TOTAL_IMAGE_BYTES @request_id = 0 @mutex = Mutex.new @initialization_mutex = Mutex.new @definitions_mutex = Mutex.new @definitions_by_name = {} @initialized = false end |
Instance Method Details
#call(name, arguments, context: nil, binding: Tool::Binding.new) ⇒ Object
Calls a generated or named Tool and returns the common Tool execution result. Names discovered through #tools resolve to their stored Definition; an undiscovered name is treated as a source name.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/little_ghost/mcp/client.rb', line 246 def call(name, arguments, context: nil, binding: Tool::Binding.new) context&.check! ensure_initialized(context:) prepared = if name.is_a?(PreparedDefinition) name elsif name.is_a?(Definition) prepare_definition(name, context:) else definition_for_call(name, context:) end definition = prepared.definition call_value = Call.new(definition:, arguments:, context:, binding:) raw = request( "tools/call", {name: definition.source_name, arguments: call_value.arguments.to_h}, context: ) result = build_result(raw, prepared:) context&.check! mapped = if @result_mapper @result_mapper.call(result, call: call_value, binding:) else default_value(result) end context&.check! tool_result(mapped, protocol_result: result) end |
#tools(context: nil, binding: Tool::Binding.new) ⇒ Object
Negotiates the protocol when needed, then provides Tool classes for the server's current definitions.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/little_ghost/mcp/client.rb', line 227 def tools(context: nil, binding: Tool::Binding.new) context&.check! ensure_initialized(context:) definitions = list_tool_definitions(context:).map do |raw| context&.check! build_definition(raw, context:).tap { context&.check! } end tools = definitions.filter_map do |definition| context&.check! build_tool(definition, binding:).tap { context&.check! } end index_tools!(tools, context:) context&.check! tools.freeze end |