module Riffer::Mcp::ToolFactory
Generates anonymous Riffer::Tool subclasses from MCP tool definitions.
Each generated class:
-
Has
.name,.description, and.parameters_schemaderived from the MCP tool definition. -
Delegates call to the MCP client’s
tools_callmethod. -
Skips Riffer’s param validation — the MCP server validates inputs.
Public Class Methods
Source
# File lib/riffer/mcp/tool_factory.rb, line 20 def self.build(manifest_name, client, tool_defs) tool_defs.map { |td| build_tool_class(manifest_name, client, td) } end
Builds one Riffer::Tool subclass per tool definition.
Tool names are prefixed with the manifest name to avoid collisions across MCP servers (e.g. +“jira__search”+). The original server-side name is available via .mcp_server_tool_name.
Source
# File lib/riffer/mcp/tool_factory.rb, line 31 def self.build_tool_class(manifest_name, client, td) prefixed = "#{sanitize_name_component(manifest_name)}__#{sanitize_name_component(td[:name])}" # steep cannot type the body of a dynamically created anonymous class: # its ivars and `self` inside define_method are unresolvable, so the # block is ignored wholesale (cf. AuthenticatedTool.wrap_one). Class.new(Riffer::Tool) do # steep:ignore:start @mcp_client = client @mcp_server_tool_name = td[:name] # Set @identifier directly so .identifier does not fall back to # class_name_to_path(nil) on this anonymous class. @identifier = prefixed define_singleton_method(:name) { prefixed } define_singleton_method(:mcp_server_tool_name) { td[:name] } define_singleton_method(:description) { td[:description] } define_singleton_method(:parameters_schema) { |strict: false| td[:input_schema] || Riffer::Tool.send(:empty_schema) } define_method(:call) do |context:, **kwargs| result = self.class.instance_variable_get(:@mcp_client).tools_call( self.class.instance_variable_get(:@mcp_server_tool_name), kwargs ) text(result) end # steep:ignore:end end end