Class: DebugMcp::Tools::ReadFile

Inherits:
MCP::Tool
  • Object
show all
Defined in:
lib/debug_mcp/tools/read_file.rb

Defined Under Namespace

Classes: FileNotFoundError

Constant Summary collapse

MAX_LINES =
500
REMOTE_CHUNK_SIZE =

Max lines to fetch per chunk via debug session (keeps output within debug gem’s width limit)

50

Class Method Summary collapse

Class Method Details

.call(path:, start_line: nil, end_line: nil, server_context:) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/debug_mcp/tools/read_file.rb', line 43

def call(path:, start_line: nil, end_line: nil, server_context:)
  # Check if we should read via the debug session (remote/Docker connection)
  client = get_client(server_context)
  if client&.remote
    return read_remote_file(client, path, start_line, end_line)
  end

  full_path = resolve_path(path, server_context)

  unless File.exist?(full_path)
    return MCP::Tool::Response.new([{ type: "text", text: "Error: File not found: #{path}" }])
  end

  lines = File.readlines(full_path)
  total_lines = lines.length

  if start_line || end_line
    start_idx = [(start_line || 1) - 1, 0].max
    end_idx = [(end_line || total_lines) - 1, total_lines - 1].min
    end_idx = [end_idx, start_idx + MAX_LINES - 1].min
    selected = lines[start_idx..end_idx]
    content = selected.map.with_index(start_idx + 1) { |line, num| "#{num}: #{line}" }.join
    header = "#{full_path} (lines #{start_idx + 1}-#{end_idx + 1} of #{total_lines})"
  else
    if lines.length > MAX_LINES
      content = lines.first(MAX_LINES).map.with_index(1) { |line, num| "#{num}: #{line}" }.join
      header = "#{full_path} (lines 1-#{MAX_LINES} of #{total_lines}, truncated)"
    else
      content = lines.map.with_index(1) { |line, num| "#{num}: #{line}" }.join
      header = "#{full_path} (#{total_lines} lines)"
    end
  end

  MCP::Tool::Response.new([{ type: "text", text: "#{header}\n\n#{content}" }])
rescue FileNotFoundError => e
  MCP::Tool::Response.new([{ type: "text", text:
    "Error: File not found: #{e.message}\n\n" \
    "This is a relative path but no active debug session is available to resolve it. " \
    "Use an absolute path, or connect to a debug session first." }])
rescue StandardError => e
  MCP::Tool::Response.new([{ type: "text", text: "Error: #{e.class}: #{e.message}" }])
end