Class: RailsMcpServer::ExecuteTool

Inherits:
BaseTool
  • Object
show all
Defined in:
lib/rails-mcp-server/tools/execute_tool.rb

Constant Summary collapse

INTERNAL_TOOLS =

Registry of available internal analyzers with their allowed parameters

{
  "project_info" => {
    class_name: "Analyzers::ProjectInfo",
    params: [:max_depth, :include_files, :detail_level]
  },
  "list_files" => {
    class_name: "Analyzers::ListFiles",
    params: [:directory, :pattern]
  },
  "get_file" => {
    class_name: "Analyzers::GetFile",
    params: [:path]
  },
  "get_routes" => {
    class_name: "Analyzers::GetRoutes",
    params: [:controller, :verb, :path_contains, :named_only, :detail_level]
  },
  "analyze_models" => {
    class_name: "Analyzers::AnalyzeModels",
    params: [:model_name, :model_names, :detail_level, :analysis_type]
  },
  "get_schema" => {
    class_name: "Analyzers::GetSchema",
    params: [:table_name, :table_names, :detail_level]
  },
  "analyze_controller_views" => {
    class_name: "Analyzers::AnalyzeControllerViews",
    params: [:controller_name, :detail_level, :analysis_type]
  },
  "analyze_environment_config" => {
    class_name: "Analyzers::AnalyzeEnvironmentConfig",
    params: []
  },
  "load_guide" => {
    class_name: "Analyzers::LoadGuide",
    params: [:library, :guide]
  }
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.available_toolsObject



112
113
114
# File 'lib/rails-mcp-server/tools/execute_tool.rb', line 112

def available_tools
  INTERNAL_TOOLS.keys.sort
end

.tool_info(name) ⇒ Object



116
117
118
# File 'lib/rails-mcp-server/tools/execute_tool.rb', line 116

def tool_info(name)
  INTERNAL_TOOLS[name]
end

Instance Method Details

#call(tool_name:, params: {}) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/rails-mcp-server/tools/execute_tool.rb', line 61

def call(tool_name:, params: {})
  tool_config = INTERNAL_TOOLS[tool_name]

  unless tool_config
    available = INTERNAL_TOOLS.keys.sort.join(", ")
    return <<~ERROR
      Unknown tool '#{tool_name}'.

      Available tools: #{available}

      Tip: Use search_tools(query: "keyword") to discover tools and their parameters.
    ERROR
  end

  # Get the analyzer class from the Analyzers module
  analyzer_class = tool_config[:class_name].split("::").reduce(RailsMcpServer) { |mod, name| mod.const_get(name) }

  # Instantiate the analyzer
  analyzer_instance = analyzer_class.new

  # Handle params passed as JSON string (from some MCP clients like Inspector)
  params ||= {}
  params = JSON.parse(params, symbolize_names: true) if params.is_a?(String)
  params = params.transform_keys(&:to_sym) if params.is_a?(Hash)

  allowed_params = tool_config[:params]
  filtered_params = params.slice(*allowed_params)

  # Log any ignored params for debugging
  ignored_params = params.keys - allowed_params
  if ignored_params.any?
    log(:debug, "Ignored unknown params for '#{tool_name}': #{ignored_params.join(", ")}")
  end

  log(:info, "Executing analyzer '#{tool_name}' with params: #{filtered_params.inspect}")

  begin
    if filtered_params.empty?
      analyzer_instance.call
    else
      analyzer_instance.call(**filtered_params)
    end
  rescue ArgumentError => e
    "Error calling '#{tool_name}': #{e.message}\n\nUse search_tools(query: '#{tool_name}', detail_level: 'full') to see required parameters."
  rescue => e
    log(:error, "Error executing analyzer '#{tool_name}': #{e.message}\n#{e.backtrace.first(5).join("\n")}")
    "Error executing '#{tool_name}': #{e.message}"
  end
end