123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::MCP::Server Private

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: lib/rubocop/mcp/server.rb

Overview

RuboCop MCP Server.

Class Method Summary

Instance Method Summary

Instance Method Details

#autocorrection_tool (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 46

def autocorrection_tool
  build_tool(
    name: 'rubocop_autocorrection',
    description: 'Autocorrect RuboCop offenses in Ruby code. ' \
                 'Provide `source_code` to correct inline code or `path` to correct files. ' \
                 'Set `safety` to false to include unsafe corrections.',
    title: "RuboCop's autocorrection",
    destructive_hint: true,
    idempotent_hint: false,
    read_only_hint: false,
    safety_required: true
  ) do |path, source_code, safety|
    run_autocorrection(path, source_code, safety)
  end
end

#build_summary(target_files, files) (private)

Note
It is useful for RuboCop’s result summary to be shown in the LLM’s responses during interactions, so the summary is returned in a form that is easy for the LLM to reason about. Since LLM execution is non-deterministic, it is also sensible to compute the summary deterministically at this stage.
[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 125

def build_summary(target_files, files)
  summary = { target_file_count: target_files.count }
  if files.first&.key?(:offenses)
    summary[:offense_count] = files.sum { |f| f[:offenses].size }
  else
    summary[:corrected_file_count] = files.count { |f| f[:corrected] }
  end
  summary
end

#build_tool(name:, description:, title:, destructive_hint:, idempotent_hint:, read_only_hint:, safety_required:) (private)

Metrics/ParameterLists

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 136

def build_tool(
  name:, description:,
  title:, destructive_hint:, idempotent_hint:, read_only_hint:, safety_required:
)
  if safety_required
    safety_property = { safety: { type: 'boolean' } }
    required = ['safety']
  else
    safety_property = {}
    required = nil
  end

  ::MCP::Tool.define(
    name: name,
    description: description,
    input_schema: {
      properties: {
        path: { type: 'string' },
        source_code: { type: 'string' }
      }.merge(safety_property),
      required: required
    }.compact,
    annotations: {
      title: title,
      destructive_hint: destructive_hint,
      idempotent_hint: idempotent_hint,
      open_world_hint: false,
      read_only_hint: read_only_hint
    }
  ) do |path: nil, source_code: nil, safety: true|
    result = yield(path, source_code, safety)

    ::MCP::Tool::Response.new([{ type: 'text', text: result }])
  end
end

#inspection_tool (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 31

def inspection_tool
  build_tool(
    name: 'rubocop_inspection',
    description: 'Inspect Ruby code for offenses. ' \
                 'Provide `source_code` to check inline code or `path` to check files.',
    title: "RuboCop's inspection",
    destructive_hint: false,
    idempotent_hint: true,
    read_only_hint: true,
    safety_required: false
  ) do |path, source_code|
    run_inspection(path, source_code)
  end
end

#process_files(path, filter_empty: false) (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 93

def process_files(path, filter_empty: false)
  target_finder = RuboCop::TargetFinder.new(@config_store, @options)
  target_files = target_finder.find(path ? [path] : [], :only_recognized_file_types)
  all_files = target_files.map { |file| yield(file, read_file(file)) }
  files = filter_empty ? all_files.reject { |f| f[:offenses]&.empty? } : all_files

  { files: files, summary: build_summary(target_files, all_files) }.to_json
end

#read_file(file) (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 102

def read_file(file)
  config = @config_store.for_file(file)
  RuboCop::ProcessedSource.from_file(
    file, config.target_ruby_version, parser_engine: config.parser_engine
  ).raw_source
rescue Errno::ENOENT
  raise RuboCop::Error, "No such file or directory: #{file}"
end

#run_autocorrection(path, source_code, safety) (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 75

def run_autocorrection(path, source_code, safety)
  command = safety ? 'rubocop.formatAutocorrects' : 'rubocop.formatAutocorrectsAll'

  if source_code
    @runtime.format(path || 'example.rb', source_code, command: command).tap do |corrected|
      write_file(path, corrected) if path
    end
  else
    process_files(path) do |file, source|
      @runtime.format(file, source, command: command).then do |corrected|
        write_file(file, corrected)

        { path: PathUtil.relative_path(file), corrected: source != corrected }
      end
    end
  end
end

#run_inspection(path, source_code) (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 62

def run_inspection(path, source_code)
  if source_code
    offenses = @runtime.offenses(path || 'example.rb', source_code, source_code.encoding)
    offenses.to_json
  else
    process_files(path, filter_empty: true) do |file, source|
      offenses = @runtime.offenses(file, source, source.encoding)

      { path: PathUtil.relative_path(file), offenses: offenses }
    end
  end
end

#start

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 18

def start
  # No `protocol_version` is specified because draft feature by default can be used.
  server = ::MCP::Server.new(
    name: 'rubocop_mcp_server',
    version: RuboCop::Version::STRING,
    tools: [inspection_tool, autocorrection_tool]
  )

  ::MCP::Server::Transports::StdioTransport.new(server).open
end

#write_file(file, content) (private)

[ GitHub ]

  
# File 'lib/rubocop/mcp/server.rb', line 111

def write_file(file, content)
  File.write(file, content)
rescue Errno::EACCES
  raise RuboCop::Error, "Permission denied: #{file}"
rescue Errno::ENOSPC
  raise RuboCop::Error, "No space left on device: #{file}"
rescue Errno::EROFS
  raise RuboCop::Error, "Read-only file system: #{file}"
end