Class: Legion::MCP::ToolAdapter

Inherits:
MCP::Tool
  • Object
show all
Extended by:
Logging::Helper
Defined in:
lib/legion/mcp/tool_adapter.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

MCP_NAME_PATTERN =
/[^a-zA-Z0-9_-]/

Class Method Summary collapse

Class Method Details

.from_legion_tool(tool_class) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/legion/mcp/tool_adapter.rb', line 15

def from_legion_tool(tool_class)
  safe_name = sanitize_tool_name(tool_class.tool_name)
  log.debug("[mcp][adapter] action=from_legion_tool tool=#{safe_name}")
  Class.new(::MCP::Tool) do
    tool_name safe_name
    description tool_class.description
    input_schema(tool_class.input_schema || { properties: {} })

    define_singleton_method(:legion_tool_class) { tool_class }

    define_singleton_method(:call) do |**params|
      result = tool_class.call(**params)
      if result.is_a?(Hash) && result[:content]
        content = result[:content].map do |c|
          { type: c[:type] || 'text', text: c[:text] || '' }
        end
        ::MCP::Tool::Response.new(content, error: result[:error] || false)
      else
        text = result.is_a?(String) ? result : Legion::JSON.dump(result)
        error = result.is_a?(Hash) ? !!result[:error] : false
        ::MCP::Tool::Response.new([{ type: 'text', text: text }], error: error)
      end
    rescue StandardError => e
      Legion::MCP::ToolAdapter.handle_exception(e, level:     :warn,
                                                   operation: 'legion.mcp.tool_adapter.from_legion_tool')
      ::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: e.message }) }], error: true)
    end
  end
end

.from_registry_entry(entry) ⇒ Object

Builds an MCP tool from a Settings::Extensions registry entry hash. If the entry contains a loaded tool_class, delegates to from_legion_tool. Otherwise builds a thin adapter from the metadata alone.



48
49
50
51
52
53
54
55
56
# File 'lib/legion/mcp/tool_adapter.rb', line 48

def from_registry_entry(entry)
  tool_class = entry[:tool_class]
  dispatch = entry[:dispatch_type]
  log.debug("[mcp][adapter] action=from_registry_entry name=#{entry[:name]} " \
            "dispatch_type=#{dispatch} has_class=#{!tool_class.nil?}")
  return from_legion_tool(tool_class) if tool_class.is_a?(Class) && tool_class.respond_to?(:tool_name)

  (entry)
end

.sanitize_tool_name(name) ⇒ Object



11
12
13
# File 'lib/legion/mcp/tool_adapter.rb', line 11

def sanitize_tool_name(name)
  name.to_s.gsub(MCP_NAME_PATTERN, '_').slice(0, 64)
end