rails-mcp-server 1.2.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +168 -166
  3. data/docs/AGENT.md +345 -0
  4. data/exe/rails-mcp-config +1411 -0
  5. data/exe/rails-mcp-server +23 -10
  6. data/exe/rails-mcp-setup-claude +1 -1
  7. data/lib/rails-mcp-server/analyzers/analyze_controller_views.rb +253 -0
  8. data/lib/rails-mcp-server/analyzers/analyze_environment_config.rb +79 -0
  9. data/lib/rails-mcp-server/analyzers/analyze_models.rb +251 -0
  10. data/lib/rails-mcp-server/analyzers/base_analyzer.rb +42 -0
  11. data/lib/rails-mcp-server/analyzers/get_file.rb +40 -0
  12. data/lib/rails-mcp-server/analyzers/get_routes.rb +212 -0
  13. data/lib/rails-mcp-server/analyzers/get_schema.rb +216 -0
  14. data/lib/rails-mcp-server/analyzers/list_files.rb +43 -0
  15. data/lib/rails-mcp-server/analyzers/load_guide.rb +84 -0
  16. data/lib/rails-mcp-server/analyzers/project_info.rb +136 -0
  17. data/lib/rails-mcp-server/tools/base_tool.rb +2 -0
  18. data/lib/rails-mcp-server/tools/execute_ruby.rb +409 -0
  19. data/lib/rails-mcp-server/tools/execute_tool.rb +115 -0
  20. data/lib/rails-mcp-server/tools/search_tools.rb +186 -0
  21. data/lib/rails-mcp-server/tools/switch_project.rb +16 -1
  22. data/lib/rails-mcp-server/version.rb +1 -1
  23. data/lib/rails_mcp_server.rb +19 -53
  24. metadata +65 -18
  25. data/lib/rails-mcp-server/extensions/resource_templating.rb +0 -182
  26. data/lib/rails-mcp-server/extensions/server_templating.rb +0 -333
  27. data/lib/rails-mcp-server/tools/analyze_controller_views.rb +0 -239
  28. data/lib/rails-mcp-server/tools/analyze_environment_config.rb +0 -427
  29. data/lib/rails-mcp-server/tools/analyze_models.rb +0 -116
  30. data/lib/rails-mcp-server/tools/get_file.rb +0 -55
  31. data/lib/rails-mcp-server/tools/get_routes.rb +0 -24
  32. data/lib/rails-mcp-server/tools/get_schema.rb +0 -141
  33. data/lib/rails-mcp-server/tools/list_files.rb +0 -54
  34. data/lib/rails-mcp-server/tools/load_guide.rb +0 -370
  35. data/lib/rails-mcp-server/tools/project_info.rb +0 -86
@@ -0,0 +1,186 @@
1
+ module RailsMcpServer
2
+ class SearchTools < BaseTool
3
+ tool_name "search_tools"
4
+
5
+ description <<~DESC
6
+ Search available Rails MCP tools by keyword or category. Use this to discover tools
7
+ before invoking them with execute_tool.
8
+
9
+ Workflow:
10
+ 1. Call search_tools to find relevant tools
11
+ 2. Call execute_tool(tool_name: "tool_name", params: {...}) to run them
12
+
13
+ Categories: models, database, routing, controllers, files, project, guides
14
+ DESC
15
+
16
+ arguments do
17
+ optional(:query).filled(:string).description(
18
+ "Search term to find matching tools (e.g., 'model', 'routes', 'schema', 'controller')"
19
+ )
20
+ optional(:category).filled(:string).description(
21
+ "Filter by category: models, database, routing, controllers, files, project, guides"
22
+ )
23
+ optional(:detail_level).filled(:string).description(
24
+ "Level of detail: 'names' (tool names only), 'summary' (names + one-line description), 'full' (complete definition with parameters). Default: 'summary'"
25
+ )
26
+ end
27
+
28
+ TOOL_CATALOG = {
29
+ "project_info" => {
30
+ category: "project",
31
+ keywords: %w[project info structure directory tree rails version],
32
+ summary: "Get Rails version, directory structure, and project overview",
33
+ parameters: [
34
+ {name: "max_depth", type: "integer", required: false, description: "Directory tree depth (default: 2, max: 5)"},
35
+ {name: "include_files", type: "boolean", required: false, description: "Include files in tree (default: true)"},
36
+ {name: "detail_level", type: "string", required: false, description: "'minimal', 'summary', or 'full' (default: 'full')"}
37
+ ]
38
+ },
39
+ "list_files" => {
40
+ category: "files",
41
+ keywords: %w[files list directory glob find search],
42
+ summary: "List files matching a pattern in a directory",
43
+ parameters: [
44
+ {name: "directory", type: "string", required: false, description: "Directory path relative to project root"},
45
+ {name: "pattern", type: "string", required: false, description: "Glob pattern (default: '*.rb')"}
46
+ ]
47
+ },
48
+ "get_file" => {
49
+ category: "files",
50
+ keywords: %w[file read content source code view],
51
+ summary: "Read the contents of a specific file",
52
+ parameters: [
53
+ {name: "path", type: "string", required: true, description: "File path relative to project root"}
54
+ ]
55
+ },
56
+ "get_routes" => {
57
+ category: "routing",
58
+ keywords: %w[routes endpoints urls api paths http verbs controller actions introspection],
59
+ summary: "List HTTP routes via Rails introspection with filtering by controller, verb, or path",
60
+ parameters: [
61
+ {name: "controller", type: "string", required: false, description: "Filter by controller name"},
62
+ {name: "verb", type: "string", required: false, description: "Filter by HTTP verb (GET, POST, PUT, PATCH, DELETE)"},
63
+ {name: "path_contains", type: "string", required: false, description: "Filter routes containing path segment"},
64
+ {name: "named_only", type: "boolean", required: false, description: "Only return named routes (default: false)"},
65
+ {name: "detail_level", type: "string", required: false, description: "'names', 'summary', or 'full' (default: 'full')"}
66
+ ]
67
+ },
68
+ "analyze_models" => {
69
+ category: "models",
70
+ keywords: %w[model active_record orm association schema database belongs_to has_many validations callbacks scopes prism introspection],
71
+ summary: "Analyze ActiveRecord models using Rails introspection and/or Prism static analysis",
72
+ parameters: [
73
+ {name: "model_name", type: "string", required: false, description: "Single model name (CamelCase)"},
74
+ {name: "model_names", type: "array", required: false, description: "Array of model names for batch analysis"},
75
+ {name: "detail_level", type: "string", required: false, description: "'names', 'associations', or 'full' (default: 'full')"},
76
+ {name: "analysis_type", type: "string", required: false, description: "'introspection' (runtime), 'static' (Prism AST), or 'full' (both). Default: 'introspection'"}
77
+ ]
78
+ },
79
+ "get_schema" => {
80
+ category: "database",
81
+ keywords: %w[schema database table column migration sql foreign_key index],
82
+ summary: "Get database schema - all tables or specific table details",
83
+ parameters: [
84
+ {name: "table_name", type: "string", required: false, description: "Specific table (snake_case, plural)"},
85
+ {name: "table_names", type: "array", required: false, description: "Array of table names for batch retrieval"},
86
+ {name: "detail_level", type: "string", required: false, description: "'tables', 'summary', or 'full' (default: 'full')"}
87
+ ]
88
+ },
89
+ "analyze_controller_views" => {
90
+ category: "controllers",
91
+ keywords: %w[controller view action template partial stimulus erb html callbacks filters before_action strong_params prism introspection],
92
+ summary: "Analyze controllers using Rails introspection and/or Prism static analysis (callbacks, filters, strong params, renders)",
93
+ parameters: [
94
+ {name: "controller_name", type: "string", required: false, description: "Specific controller to analyze"},
95
+ {name: "detail_level", type: "string", required: false, description: "'names', 'summary', or 'full' (default: 'full')"},
96
+ {name: "analysis_type", type: "string", required: false, description: "'introspection' (runtime), 'static' (Prism AST), or 'full' (both). Default: 'introspection'"}
97
+ ]
98
+ },
99
+ "analyze_environment_config" => {
100
+ category: "project",
101
+ keywords: %w[environment config configuration development production test settings],
102
+ summary: "Compare environment configurations and find inconsistencies",
103
+ parameters: []
104
+ },
105
+ "load_guide" => {
106
+ category: "guides",
107
+ keywords: %w[guide documentation rails turbo stimulus kamal docs help],
108
+ summary: "Load Rails, Turbo, Stimulus, or Kamal documentation guides",
109
+ parameters: [
110
+ {name: "guides", type: "string", required: true, description: "Library: 'rails', 'turbo', 'stimulus', 'kamal', 'custom'"},
111
+ {name: "guide", type: "string", required: false, description: "Specific guide name to load"}
112
+ ]
113
+ }
114
+ }.freeze
115
+
116
+ CATEGORIES = %w[models database routing controllers files project guides].freeze
117
+
118
+ def call(query: nil, category: nil, detail_level: "summary")
119
+ detail_level = "summary" unless %w[names summary full].include?(detail_level)
120
+
121
+ # Filter tools
122
+ filtered_tools = TOOL_CATALOG.dup
123
+
124
+ if category && CATEGORIES.include?(category.downcase)
125
+ filtered_tools = filtered_tools.select { |_, info| info[:category] == category.downcase }
126
+ end
127
+
128
+ if query && !query.strip.empty?
129
+ query_terms = query.downcase.split(/\s+/)
130
+ filtered_tools = filtered_tools.select do |name, info|
131
+ searchable = [name, info[:category], info[:summary], *info[:keywords]].join(" ").downcase
132
+ query_terms.all? { |term| searchable.include?(term) }
133
+ end
134
+ end
135
+
136
+ if filtered_tools.empty?
137
+ return "No tools found matching your criteria. Available categories: #{CATEGORIES.join(", ")}"
138
+ end
139
+
140
+ format_results(filtered_tools, detail_level)
141
+ end
142
+
143
+ private
144
+
145
+ def format_results(tools, detail_level)
146
+ case detail_level
147
+ when "names"
148
+ output = ["Available tools (invoke via execute_tool):\n"]
149
+ output << tools.keys.sort.join("\n")
150
+ output.join
151
+
152
+ when "summary"
153
+ output = ["Available tools (invoke via execute_tool):\n"]
154
+ tools.sort_by { |name, _| name }.each do |name, info|
155
+ output << " #{name} [#{info[:category]}]"
156
+ output << " #{info[:summary]}"
157
+ output << ""
158
+ end
159
+ output << "Example: execute_tool(tool_name: \"analyze_models\", params: { model_name: \"User\", analysis_type: \"full\" })"
160
+ output.join("\n")
161
+
162
+ when "full"
163
+ output = ["Available tools (invoke via execute_tool):\n"]
164
+ tools.sort_by { |name, _| name }.each do |name, info|
165
+ output << " #{name}"
166
+ output << " Category: #{info[:category]}"
167
+ output << " #{info[:summary]}"
168
+ output << " Keywords: #{info[:keywords].join(", ")}"
169
+
170
+ if info[:parameters].any?
171
+ output << " Parameters:"
172
+ info[:parameters].each do |param|
173
+ req = param[:required] ? "required" : "optional"
174
+ output << " - #{param[:name]} (#{param[:type]}, #{req}): #{param[:description]}"
175
+ end
176
+ else
177
+ output << " Parameters: none"
178
+ end
179
+ output << ""
180
+ end
181
+ output << "Usage: execute_tool(tool_name: \"<name>\", params: { ... })"
182
+ output.join("\n")
183
+ end
184
+ end
185
+ end
186
+ end
@@ -8,13 +8,28 @@ module RailsMcpServer
8
8
  required(:project_name).filled(:string).description("Name of the project as defined in the projects.yml file (case-sensitive)")
9
9
  end
10
10
 
11
+ QUICK_START = <<~GUIDE
12
+
13
+ Quick Start:
14
+ • Get project overview: execute_tool("project_info")
15
+ • Read a file: execute_ruby("puts read_file('config/routes.rb')")
16
+ • Find files: execute_ruby("puts Dir.glob('app/models/*.rb').join('\\n')")
17
+ • Analyze models: execute_tool("analyze_models", { model_name: "User" })
18
+ • Get routes: execute_tool("get_routes")
19
+ • Get schema: execute_tool("get_schema", { table_name: "users" })
20
+ • Search available tools: search_tools()
21
+
22
+ Helpers in execute_ruby: read_file(path), file_exists?(path), list_files(pattern), project_root
23
+ Note: Always use `puts` in execute_ruby to see output.
24
+ GUIDE
25
+
11
26
  def call(project_name:)
12
27
  if projects.key?(project_name)
13
28
  self.current_project = project_name
14
29
  self.active_project_path = File.expand_path(projects[project_name])
15
30
  log(:info, "Switched to project: #{project_name} at path: #{active_project_path}")
16
31
 
17
- "Switched to project: #{project_name} at path: #{active_project_path}"
32
+ "Switched to project: #{project_name} at path: #{active_project_path}\n#{QUICK_START}"
18
33
  else
19
34
  log(:warn, "Project not found: #{project_name}")
20
35
 
@@ -1,3 +1,3 @@
1
1
  module RailsMcpServer
2
- VERSION = "1.2.3"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -4,29 +4,35 @@ require "forwardable"
4
4
  require "open3"
5
5
  require_relative "rails-mcp-server/version"
6
6
  require_relative "rails-mcp-server/config"
7
- require_relative "rails-mcp-server/extensions/resource_templating"
8
- require_relative "rails-mcp-server/extensions/server_templating"
9
7
  require_relative "rails-mcp-server/utilities/run_process"
8
+
9
+ # MCP Tools (registered with FastMCP)
10
10
  require_relative "rails-mcp-server/tools/base_tool"
11
- require_relative "rails-mcp-server/tools/project_info"
12
- require_relative "rails-mcp-server/tools/list_files"
13
- require_relative "rails-mcp-server/tools/get_file"
14
- require_relative "rails-mcp-server/tools/get_routes"
15
- require_relative "rails-mcp-server/tools/analyze_models"
16
- require_relative "rails-mcp-server/tools/get_schema"
17
- require_relative "rails-mcp-server/tools/analyze_controller_views"
18
- require_relative "rails-mcp-server/tools/analyze_environment_config"
19
11
  require_relative "rails-mcp-server/tools/switch_project"
20
- require_relative "rails-mcp-server/tools/load_guide"
12
+ require_relative "rails-mcp-server/tools/search_tools"
13
+ require_relative "rails-mcp-server/tools/execute_tool"
14
+ require_relative "rails-mcp-server/tools/execute_ruby"
15
+
16
+ # Analyzers (internal, invoked via execute_tool)
17
+ require_relative "rails-mcp-server/analyzers/base_analyzer"
18
+ require_relative "rails-mcp-server/analyzers/project_info"
19
+ require_relative "rails-mcp-server/analyzers/list_files"
20
+ require_relative "rails-mcp-server/analyzers/get_file"
21
+ require_relative "rails-mcp-server/analyzers/get_routes"
22
+ require_relative "rails-mcp-server/analyzers/get_schema"
23
+ require_relative "rails-mcp-server/analyzers/analyze_models"
24
+ require_relative "rails-mcp-server/analyzers/analyze_controller_views"
25
+ require_relative "rails-mcp-server/analyzers/analyze_environment_config"
26
+ require_relative "rails-mcp-server/analyzers/load_guide"
27
+
28
+ # Resources
21
29
  require_relative "rails-mcp-server/resources/base_resource"
22
-
23
30
  require_relative "rails-mcp-server/resources/guide_content_formatter"
24
31
  require_relative "rails-mcp-server/resources/guide_error_handler"
25
32
  require_relative "rails-mcp-server/resources/guide_file_finder"
26
33
  require_relative "rails-mcp-server/resources/guide_loader_template"
27
34
  require_relative "rails-mcp-server/resources/guide_manifest_operations"
28
35
  require_relative "rails-mcp-server/resources/guide_framework_contract"
29
-
30
36
  require_relative "rails-mcp-server/resources/rails_guides_resource"
31
37
  require_relative "rails-mcp-server/resources/rails_guides_resources"
32
38
  require_relative "rails-mcp-server/resources/stimulus_guides_resource"
@@ -59,45 +65,5 @@ module RailsMcpServer
59
65
 
60
66
  @config.logger.add(log_level, message)
61
67
  end
62
-
63
- # NOTE: This needs to be removed once FastMcp provides official support for URI templating
64
- # Setup method to initialize FastMcp::Resource extensions
65
- # Call this after the gem is loaded to enable URI templating
66
- def setup_resource_extensions!
67
- Extensions::ResourceExtensionSetup.setup!
68
- end
69
-
70
- # Check if resource extensions are loaded
71
- def resource_extensions_loaded?
72
- Extensions::ResourceExtensionSetup.setup_complete?
73
- end
74
-
75
- # Setup method to initialize FastMcp::Server extensions
76
- # This is called automatically by resource extension setup
77
- def setup_server_extensions!
78
- Extensions::ServerExtensionSetup.setup!
79
- end
80
-
81
- # Check if server extensions are loaded
82
- def server_extensions_loaded?
83
- Extensions::ServerExtensionSetup.setup_complete?
84
- end
85
-
86
- # Setup all extensions at once
87
- def setup_extensions!
88
- setup_resource_extensions!
89
- # Server extensions are setup automatically by resource extensions
90
- end
91
-
92
- # Check if all extensions are loaded
93
- def extensions_loaded?
94
- resource_extensions_loaded? && server_extensions_loaded?
95
- end
96
68
  end
97
-
98
- class Error < StandardError; end
99
-
100
- # Auto-setup extensions when the module is loaded
101
- # This ensures extensions are available immediately
102
- setup_resource_extensions!
103
69
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-mcp-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Alberto Chávez Cárdenas
@@ -29,42 +29,42 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 1.4.0
32
+ version: 1.6.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 1.4.0
39
+ version: 1.6.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rack
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 3.1.12
46
+ version: 3.2.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 3.1.12
53
+ version: 3.2.0
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: puma
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: 6.6.0
60
+ version: 7.1.0
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 6.6.0
67
+ version: 7.1.0
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: logger
70
70
  requirement: !ruby/object:Gem::Requirement
@@ -93,10 +93,53 @@ dependencies:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: minitest
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '5.25'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '5.25'
110
+ - !ruby/object:Gem::Dependency
111
+ name: minitest-reporters
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.7'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.7'
124
+ - !ruby/object:Gem::Dependency
125
+ name: mocha
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '2.7'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.7'
96
138
  description: A Ruby implementation of Model Context Protocol server for Rails projects
97
139
  email:
98
140
  - mario.chavez@gmail.com
99
141
  executables:
142
+ - rails-mcp-config
100
143
  - rails-mcp-server
101
144
  - rails-mcp-server-download-resources
102
145
  - rails-mcp-setup-claude
@@ -107,13 +150,23 @@ files:
107
150
  - LICENSE.txt
108
151
  - README.md
109
152
  - config/resources.yml
153
+ - 'docs/AGENT.md '
110
154
  - docs/RESOURCES.md
155
+ - exe/rails-mcp-config
111
156
  - exe/rails-mcp-server
112
157
  - exe/rails-mcp-server-download-resources
113
158
  - exe/rails-mcp-setup-claude
159
+ - lib/rails-mcp-server/analyzers/analyze_controller_views.rb
160
+ - lib/rails-mcp-server/analyzers/analyze_environment_config.rb
161
+ - lib/rails-mcp-server/analyzers/analyze_models.rb
162
+ - lib/rails-mcp-server/analyzers/base_analyzer.rb
163
+ - lib/rails-mcp-server/analyzers/get_file.rb
164
+ - lib/rails-mcp-server/analyzers/get_routes.rb
165
+ - lib/rails-mcp-server/analyzers/get_schema.rb
166
+ - lib/rails-mcp-server/analyzers/list_files.rb
167
+ - lib/rails-mcp-server/analyzers/load_guide.rb
168
+ - lib/rails-mcp-server/analyzers/project_info.rb
114
169
  - lib/rails-mcp-server/config.rb
115
- - lib/rails-mcp-server/extensions/resource_templating.rb
116
- - lib/rails-mcp-server/extensions/server_templating.rb
117
170
  - lib/rails-mcp-server/helpers/resource_base.rb
118
171
  - lib/rails-mcp-server/helpers/resource_downloader.rb
119
172
  - lib/rails-mcp-server/helpers/resource_importer.rb
@@ -134,17 +187,11 @@ files:
134
187
  - lib/rails-mcp-server/resources/stimulus_guides_resources.rb
135
188
  - lib/rails-mcp-server/resources/turbo_guides_resource.rb
136
189
  - lib/rails-mcp-server/resources/turbo_guides_resources.rb
137
- - lib/rails-mcp-server/tools/analyze_controller_views.rb
138
- - lib/rails-mcp-server/tools/analyze_environment_config.rb
139
- - lib/rails-mcp-server/tools/analyze_models.rb
140
190
  - lib/rails-mcp-server/tools/base_tool.rb
141
- - lib/rails-mcp-server/tools/get_file.rb
191
+ - lib/rails-mcp-server/tools/execute_ruby.rb
192
+ - lib/rails-mcp-server/tools/execute_tool.rb
142
193
  - lib/rails-mcp-server/tools/get_model.rb
143
- - lib/rails-mcp-server/tools/get_routes.rb
144
- - lib/rails-mcp-server/tools/get_schema.rb
145
- - lib/rails-mcp-server/tools/list_files.rb
146
- - lib/rails-mcp-server/tools/load_guide.rb
147
- - lib/rails-mcp-server/tools/project_info.rb
194
+ - lib/rails-mcp-server/tools/search_tools.rb
148
195
  - lib/rails-mcp-server/tools/switch_project.rb
149
196
  - lib/rails-mcp-server/utilities/run_process.rb
150
197
  - lib/rails-mcp-server/version.rb
@@ -1,182 +0,0 @@
1
- module RailsMcpServer
2
- module Extensions
3
- # Extension module to add URI templating capabilities to FastMcp::Resource
4
- # Uses module prepending for clean method override behavior
5
- module ResourceTemplating
6
- # Class methods to be prepended to the singleton class
7
- module ClassMethods
8
- attr_reader :template_params
9
-
10
- def variabilized_uri(params = {})
11
- addressable_template.partial_expand(params).pattern
12
- end
13
-
14
- def addressable_template
15
- @addressable_template ||= Addressable::Template.new(uri)
16
- end
17
-
18
- def template_variables
19
- addressable_template.variables
20
- end
21
-
22
- def templated?
23
- template_variables.any?
24
- end
25
-
26
- def non_templated?
27
- !templated?
28
- end
29
-
30
- def match(uri)
31
- addressable_template.match(uri)
32
- end
33
-
34
- def initialize_from_uri(uri)
35
- new(params_from_uri(uri))
36
- end
37
-
38
- def params_from_uri(uri)
39
- match(uri).mapping.transform_keys(&:to_sym)
40
- end
41
-
42
- def instance(uri = self.uri)
43
- @instances ||= {}
44
- @instances[uri] ||= begin
45
- resource_class = Class.new(self)
46
- params = params_from_uri(uri)
47
- resource_class.instance_variable_set(:@params, params)
48
-
49
- resource_class.define_singleton_method(:instance) do
50
- @instance ||= begin
51
- instance = new
52
- instance.instance_variable_set(:@params, params)
53
- instance
54
- end
55
- end
56
-
57
- resource_class.instance
58
- end
59
- end
60
-
61
- def params
62
- @params || {}
63
- end
64
-
65
- def name
66
- return resource_name if resource_name
67
- super
68
- end
69
-
70
- def metadata
71
- if templated?
72
- {
73
- uriTemplate: uri,
74
- name: resource_name,
75
- description: description,
76
- mimeType: mime_type
77
- }.compact
78
- else
79
- super
80
- end
81
- end
82
- end
83
-
84
- # Instance methods to be prepended
85
- module InstanceMethods
86
- def initialize
87
- @params = self.class.params
88
- super if defined?(super)
89
- end
90
-
91
- def params
92
- @params || self.class.params
93
- end
94
-
95
- def name
96
- self.class.resource_name
97
- end
98
- end
99
-
100
- # Called when this module is prepended to a class
101
- def self.prepended(base)
102
- base.singleton_class.prepend(ClassMethods)
103
- base.prepend(InstanceMethods)
104
- end
105
- end
106
-
107
- # Main setup class for resource extensions
108
- class ResourceExtensionSetup
109
- class << self
110
- def setup!
111
- return if @setup_complete
112
-
113
- ensure_dependencies_loaded!
114
- apply_extensions!
115
-
116
- @setup_complete = true
117
- RailsMcpServer.log(:info, "FastMcp::Resource extensions loaded successfully")
118
- rescue => e
119
- RailsMcpServer.log(:error, "Failed to setup resource extensions: #{e.message}")
120
- raise
121
- end
122
-
123
- def reset!
124
- @setup_complete = false
125
- end
126
-
127
- def setup_complete?
128
- @setup_complete || false
129
- end
130
-
131
- private
132
-
133
- def ensure_dependencies_loaded!
134
- # Check that FastMcp::Resource exists
135
- unless defined?(FastMcp::Resource)
136
- begin
137
- require "fast-mcp"
138
- rescue LoadError => e
139
- raise LoadError, "fast-mcp gem is required but not available. Ensure it's in your Gemfile: #{e.message}"
140
- end
141
- end
142
-
143
- # Verify the expected interface exists
144
- unless FastMcp::Resource.respond_to?(:uri)
145
- raise "FastMcp::Resource doesn't have expected interface. Check fast-mcp gem version."
146
- end
147
-
148
- # Load addressable template dependency
149
- begin
150
- require "addressable/template"
151
- rescue LoadError => e
152
- raise LoadError, "addressable gem is required for URI templating: #{e.message}"
153
- end
154
-
155
- # Optional: Version checking
156
- if defined?(FastMcp::VERSION)
157
- version = Gem::Version.new(FastMcp::VERSION)
158
- minimum_version = Gem::Version.new("1.4.0")
159
-
160
- if version < minimum_version
161
- RailsMcpServer.log(:warn, "FastMcp version #{FastMcp::VERSION} detected. Extensions tested with #{minimum_version}+")
162
- end
163
- end
164
- end
165
-
166
- def apply_extensions!
167
- # Apply extensions to FastMcp::Resource
168
- FastMcp::Resource.prepend(ResourceTemplating)
169
-
170
- # Also ensure our BaseResource gets the extensions
171
- if defined?(RailsMcpServer::BaseResource)
172
- # BaseResource already inherits from FastMcp::Resource, so it gets extensions automatically
173
- RailsMcpServer.log(:debug, "BaseResource will inherit templating extensions")
174
- end
175
-
176
- # Setup server extensions as well
177
- RailsMcpServer::Extensions::ServerExtensionSetup.setup!
178
- end
179
- end
180
- end
181
- end
182
- end