Class: Rubydex::MCPServer::BaseTool

Inherits:
Tool
  • Object
show all
Defined in:
lib/rubydex/mcp_server/tools/base_tool.rb

Constant Summary collapse

DEFAULT_LIMIT =
50

Instance Method Summary collapse

Methods inherited from Tool

description, inherited, input_schema, to_h, tool_name, tools_by_name

Constructor Details

#initialize(graph) ⇒ BaseTool

: (Graph) -> void



13
14
15
16
17
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 13

def initialize(graph)
  super()
  @graph = graph
  @root_path = graph.workspace_path
end

Instance Method Details

#declaration_kind(declaration) ⇒ Object

: (Declaration) -> String



25
26
27
28
29
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 25

def declaration_kind(declaration)
  return "<TODO>" if declaration.is_a?(Rubydex::Todo)

  declaration.class.name.delete_prefix("Rubydex::")
end

#display_location(location) ⇒ Object

: (Location) -> Hash



70
71
72
73
74
75
76
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 70

def display_location(location)
  display = location.to_display
  {
    path: format_path(display.uri),
    line: display.start_line,
  }
end

#document_for_path(file_path) ⇒ Object

: (String) -> Document?



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 56

def document_for_path(file_path)
  absolute_target = if Pathname.new(file_path).absolute?
    file_path
  else
    File.join(@root_path, file_path)
  end
  canonical_target = File.realpath(absolute_target)
  @graph.documents.find do |document|
    path = file_path_for_uri(document.uri)
    path && File.expand_path(path) == canonical_target
  end
end

#file_path_for_uri(uri) ⇒ Object

: (String) -> String?



44
45
46
47
48
49
50
51
52
53
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 44

def file_path_for_uri(uri)
  parsed = URI.parse(uri)
  return unless parsed.scheme == "file"

  path = URI.decode_uri_component(parsed.path)
  path.delete_prefix!("/") if Gem.win_platform?
  path
rescue URI::InvalidURIError, ArgumentError
  nil
end

#format_path(uri) ⇒ Object

: (String) -> String



32
33
34
35
36
37
38
39
40
41
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 32

def format_path(uri)
  path = file_path_for_uri(uri)
  return uri unless path

  absolute_path = File.expand_path(path)
  absolute_root = File.expand_path(@root_path)
  relative_path = Pathname.new(absolute_path).relative_path_from(Pathname.new(absolute_root)).to_s

  relative_path.start_with?("..") ? absolute_path : relative_path
end

#lookup_declaration(name) ⇒ Object

: (String) -> Declaration | Error



97
98
99
100
101
102
103
104
105
106
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 97

def lookup_declaration(name)
  declaration = @graph[name]
  return declaration if declaration

  Error.new(
    "not_found",
    "Declaration '#{name}' not found",
    "Try search_declarations with a partial name to find the correct FQN",
  )
end

#paginate(items, offset, limit, max_limit) ⇒ Object

: (Enumerable, Integer?, Integer?, Integer) -> [Array, Integer]



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 79

def paginate(items, offset, limit, max_limit)
  offset = (offset || 0).to_i
  offset = 0 unless offset.positive?
  limit = (limit || DEFAULT_LIMIT).to_i
  limit = DEFAULT_LIMIT unless limit.positive?
  limit = [limit, max_limit].min

  page = []
  index = 0
  items.each do |item|
    page << item if index >= offset && page.length < limit
    index += 1
  end

  [page, index]
end

#response(payload) ⇒ Object

: (Hash | Error) -> Tool::Response



20
21
22
# File 'lib/rubydex/mcp_server/tools/base_tool.rb', line 20

def response(payload)
  Tool::Response.new([{ type: "text", text: JSON.generate(payload) }])
end