Class: Rubino::MCP::MCPToolWrapper

Inherits:
Tools::Base show all
Defined in:
lib/rubino/mcp/mcp_tool_wrapper.rb

Overview

Wraps an MCP tool (from ruby_llm-mcp) into the Rubino::Tools::Base interface. This allows MCP tools to be used seamlessly alongside built-in tools.

Instance Attribute Summary collapse

Attributes inherited from Tools::Base

#cancel_token, #read_tracker, #stream_chunk

Instance Method Summary collapse

Methods inherited from Tools::Base

#cancellation_requested?, #config_key, #emit_chunk, #risky?, workspace_root, workspace_roots

Constructor Details

#initialize(mcp_tool, server_name:) ⇒ MCPToolWrapper

Returns a new instance of MCPToolWrapper.



10
11
12
13
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 10

def initialize(mcp_tool, server_name:)
  @mcp_tool = mcp_tool
  @server_name = server_name
end

Instance Attribute Details

#mcp_toolObject (readonly)

Returns the value of attribute mcp_tool.



8
9
10
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 8

def mcp_tool
  @mcp_tool
end

#server_nameObject (readonly)

Returns the value of attribute server_name.



8
9
10
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 8

def server_name
  @server_name
end

Instance Method Details

#call(arguments) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 39

def call(arguments)
  result = @mcp_tool.execute(**symbolize_keys(arguments))
  # ruby_llm-mcp reports tool failures by RETURNING `{ error: "…" }`
  # instead of raising. Map both failure paths onto the registry's
  # "Error: …" convention (Tools::Result#errorish?) so an errored MCP
  # call renders ✗ like any built-in tool, not "✓ done" (#172).
  error = result[:error] || result["error"] if result.is_a?(Hash)
  return "Error: MCP tool #{@server_name}/#{@mcp_tool.name}: #{error}" if error

  result.to_s
rescue StandardError => e
  "Error: MCP tool #{@server_name}/#{@mcp_tool.name}: #{e.message}"
end

#descriptionObject



20
21
22
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 20

def description
  @mcp_tool.description
end

#input_schemaObject



24
25
26
27
28
29
30
31
32
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 24

def input_schema
  # The server-advertised JSON schema lives in RubyLLM::MCP::Tool#params_schema.
  # The inherited RubyLLM::Tool#parameters DSL accessor is ALWAYS empty for
  # MCP tools — forwarding it sent every tool to the model with `parameters:
  # {}`, so the model had to guess argument names and every call failed
  # server-side validation with -32602 (#170).
  schema = @mcp_tool.params_schema if @mcp_tool.respond_to?(:params_schema)
  schema || { type: "object", properties: {} }
end

#nameObject



15
16
17
18
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 15

def name
  # Prefix with server name to avoid collisions
  "#{@server_name}_#{@mcp_tool.name}"
end

#risk_levelObject



34
35
36
37
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 34

def risk_level
  # MCP tools are external, default to medium risk
  :medium
end

#to_tool_definitionObject

Override to provide the raw MCP tool definition for LLM



54
55
56
57
58
59
60
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 54

def to_tool_definition
  {
    name: name,
    description: description,
    parameters: input_schema
  }
end