Class: Fast::McpServer

Inherits:
Object
  • Object
show all
Defined in:
lib/fast/mcp_server.rb

Overview

Implements the Model Context Protocol (MCP) server over STDIO.

Constant Summary collapse

TOOLS =
[
  {
    name: 'validate_fast_pattern',
    description: 'Validate a Fast AST pattern. Returns true if valid, or a specific syntax error message if invalid.',
    inputSchema: {
      type: 'object',
      properties: {
        pattern: { type: 'string', description: 'Fast AST pattern to validate.' }
      },
      required: ['pattern']
    }
  },
  {
    name: 'search_ruby_ast',
    description: 'Search Ruby files using a Fast AST pattern. Returns file, line range, and source. Use show_ast=true only when you need the s-expression.',
    inputSchema: {
      type: 'object',
      properties: {
        pattern: { type: 'string', description: 'Fast AST pattern, e.g. "(def match?)" or "(send nil :raise ...)".' },
        paths:   { type: 'array', items: { type: 'string' }, description: 'Files or directories to search.' },
        show_ast: { type: 'boolean', description: 'Include s-expression AST in results (default: false).' }
      },
      required: ['pattern', 'paths']
    }
  },
  {
    name: 'ruby_method_source',
    description: 'Extract source of a Ruby method by name across files. Optionally filter by class name.',
    inputSchema: {
      type: 'object',
      properties: {
        method_name: { type: 'string', description: 'Method name, e.g. "initialize".' },
        paths:       { type: 'array', items: { type: 'string' }, description: 'Files or directories to search.' },
        class_name:  { type: 'string', description: 'Optional class name to restrict results, e.g. "Matcher".' },
        show_ast:    { type: 'boolean', description: 'Include s-expression AST in results (default: false).' }
      },
      required: ['method_name', 'paths']
    }
  },
  {
    name: 'ruby_class_source',
    description: 'Extract the full source of a Ruby class by name.',
    inputSchema: {
      type: 'object',
      properties: {
        class_name: { type: 'string', description: 'Class name to extract, e.g. "Rewriter".' },
        paths:      { type: 'array', items: { type: 'string' }, description: 'Files or directories to search.' },
        show_ast:   { type: 'boolean', description: 'Include s-expression AST in results (default: false).' }
      },
      required: ['class_name', 'paths']
    }
  },
  {
    name: 'rewrite_ruby',
    description: 'Apply a Fast pattern replacement to Ruby source code. Returns the rewritten source. Does NOT write to disk.',
    inputSchema: {
      type: 'object',
      properties: {
        source:      { type: 'string', description: 'Ruby source code to rewrite.' },
        pattern:     { type: 'string', description: 'Fast AST pattern to match nodes for replacement.' },
        replacement: { type: 'string', description: 'Ruby expression to replace matched node source with.' }
      },
      required: ['source', 'pattern', 'replacement']
    }
  },
  {
    name: 'rewrite_ruby_file',
    description: 'Apply a Fast pattern replacement to a Ruby file in-place. Returns lines changed and a diff. Use rewrite_ruby first to preview.',
    inputSchema: {
      type: 'object',
      properties: {
        file:        { type: 'string', description: 'Path to the Ruby file to rewrite.' },
        pattern:     { type: 'string', description: 'Fast AST pattern to match nodes for replacement.' },
        replacement: { type: 'string', description: 'Ruby expression to replace matched node source with.' }
      },
      required: ['file', 'pattern', 'replacement']
    }
  },
  {
    name: 'run_fast_experiment',
    description: 'Propose and execute a Fast experiment to safely refactor code. The experiment is validated against a policy command (e.g. tests) and only successful rewrites are applied. Always use {file} in the policy command to refer to the modified test file.',
    inputSchema: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Name of the experiment, e.g. "RSpec/UseBuildStubbed"' },
        lookup: { type: 'string', description: 'Folder or file to target, e.g. "spec"' },
        search: { type: 'string', description: 'Fast AST search pattern to find nodes.' },
        edit: { type: 'string', description: 'Ruby code to evaluate in Rewriter context. Has access to `node` variable. Example: `replace(node.loc.expression, "build_stubbed")`' },
        policy: { type: 'string', description: 'Shell command returning exit status 0 on success. Uses {file} for the temporary file created during the rewrite round. Example: `bin/spring rspec --fail-fast {file}`' }
      },
      required: ['name', 'lookup', 'search', 'edit', 'policy']
    }
  }
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.run!Object



108
109
110
# File 'lib/fast/mcp_server.rb', line 108

def self.run!
  new.run
end

Instance Method Details

#runObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/fast/mcp_server.rb', line 112

def run
  STDOUT.sync = true

  while (line = STDIN.gets)
    line = line.strip
    next if line.empty?

    begin
      request = JSON.parse(line)
      handle_request(request)
    rescue JSON::ParserError => e
      write_error(nil, -32700, 'Parse error', e.message)
    rescue StandardError => e
      write_error(request&.fetch('id', nil), -32603, 'Internal error', e.message)
    end
  end
end