Class: Rubino::MCP::MCPToolWrapper
- Inherits:
-
Tools::Base
- Object
- Tools::Base
- Rubino::MCP::MCPToolWrapper
- 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.
Constant Summary collapse
- MAX_NAME_LENGTH =
Cap on the (prefixed) tool name length. A misbehaving MCP server can advertise an absurdly long tool name (e.g. 20k chars), which would be registered uncapped, blow up the
toolstable, and 400 at the provider. 64
Instance Attribute Summary collapse
-
#mcp_tool ⇒ Object
readonly
Returns the value of attribute mcp_tool.
-
#server_name ⇒ Object
readonly
Returns the value of attribute server_name.
Attributes inherited from Tools::Base
#cancel_token, #read_tracker, #stream_chunk, #stream_kind
Instance Method Summary collapse
-
#bare_name ⇒ Object
The original, UNPREFIXED tool name the server advertised (#name returns the collision-safe
<server>_<tool>registry/model-facing name). - #call(arguments) ⇒ Object
- #description ⇒ Object
-
#display_name ⇒ Object
The display label for the live tool card and approval card:
"<bare_tool> (mcp:<server>)"— e.g. -
#initialize(mcp_tool, server_name:) ⇒ MCPToolWrapper
constructor
A new instance of MCPToolWrapper.
- #input_schema ⇒ Object
-
#mcp? ⇒ Boolean
True: this tool's code runs on an external MCP server.
-
#mcp_server ⇒ Object
The MCP server this tool is provided by, used in the display marker.
- #name ⇒ Object
- #risk_level ⇒ Object
-
#to_tool_definition ⇒ Object
Override to provide the raw MCP tool definition for LLM.
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.
15 16 17 18 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 15 def initialize(mcp_tool, server_name:) @mcp_tool = mcp_tool @server_name = server_name end |
Instance Attribute Details
#mcp_tool ⇒ Object (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_name ⇒ Object (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
#bare_name ⇒ Object
The original, UNPREFIXED tool name the server advertised (#name returns
the collision-safe <server>_<tool> registry/model-facing name).
63 64 65 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 63 def @mcp_tool.name end |
#call(arguments) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 74 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.}" end |
#description ⇒ Object
27 28 29 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 27 def description @mcp_tool.description end |
#display_name ⇒ Object
The display label for the live tool card and approval card:
"<bare_tool> (mcp:<server>)" — e.g. echo (mcp:chaos). The
model-facing #name (chaos_echo) is unchanged; this is display-only.
70 71 72 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 70 def display_name "#{} (mcp:#{@server_name})" end |
#input_schema ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 31 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) # Coerce anything that isn't a Hash (nil, or a truthy non-Hash like a # string) to a valid empty object schema. A server advertising a # non-Hash `inputSchema` would otherwise poison the whole wire tool # list and 400 every subsequent model call (S1-MCP-1). schema.is_a?(Hash) ? schema : { type: "object", properties: {} } end |
#mcp? ⇒ Boolean
True: this tool's code runs on an external MCP server. The display layer reads this (NOT the name shape) to mark the call/approval card.
52 53 54 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 52 def mcp? true end |
#mcp_server ⇒ Object
The MCP server this tool is provided by, used in the display marker.
57 58 59 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 57 def mcp_server @server_name end |
#name ⇒ Object
20 21 22 23 24 25 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 20 def name # Prefix with server name to avoid collisions. Cap the length so a # hostile/buggy server can't register a giant name that breaks the # `tools` table or 400s the provider (S1-MCP-2). "#{@server_name}_#{@mcp_tool.name}"[0, MAX_NAME_LENGTH] end |
#risk_level ⇒ Object
45 46 47 48 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 45 def risk_level # MCP tools are external, default to medium risk :medium end |
#to_tool_definition ⇒ Object
Override to provide the raw MCP tool definition for LLM
89 90 91 92 93 94 95 |
# File 'lib/rubino/mcp/mcp_tool_wrapper.rb', line 89 def to_tool_definition { name: name, description: description, parameters: input_schema } end |