rails-mcp-server 1.1.4 → 1.2.1

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/CHANGELOG.md +216 -0
  3. data/README.md +156 -46
  4. data/config/resources.yml +203 -0
  5. data/docs/RESOURCES.md +339 -0
  6. data/exe/rails-mcp-server +8 -5
  7. data/exe/rails-mcp-server-download-resources +120 -0
  8. data/lib/rails-mcp-server/config.rb +7 -1
  9. data/lib/rails-mcp-server/extensions/resource_templating.rb +182 -0
  10. data/lib/rails-mcp-server/extensions/server_templating.rb +333 -0
  11. data/lib/rails-mcp-server/helpers/resource_base.rb +143 -0
  12. data/lib/rails-mcp-server/helpers/resource_downloader.rb +104 -0
  13. data/lib/rails-mcp-server/helpers/resource_importer.rb +113 -0
  14. data/lib/rails-mcp-server/resources/base_resource.rb +7 -0
  15. data/lib/rails-mcp-server/resources/custom_guides_resource.rb +54 -0
  16. data/lib/rails-mcp-server/resources/custom_guides_resources.rb +37 -0
  17. data/lib/rails-mcp-server/resources/guide_content_formatter.rb +130 -0
  18. data/lib/rails-mcp-server/resources/guide_error_handler.rb +85 -0
  19. data/lib/rails-mcp-server/resources/guide_file_finder.rb +100 -0
  20. data/lib/rails-mcp-server/resources/guide_framework_contract.rb +65 -0
  21. data/lib/rails-mcp-server/resources/guide_loader_template.rb +122 -0
  22. data/lib/rails-mcp-server/resources/guide_manifest_operations.rb +52 -0
  23. data/lib/rails-mcp-server/resources/kamal_guides_resource.rb +80 -0
  24. data/lib/rails-mcp-server/resources/kamal_guides_resources.rb +110 -0
  25. data/lib/rails-mcp-server/resources/rails_guides_resource.rb +29 -0
  26. data/lib/rails-mcp-server/resources/rails_guides_resources.rb +37 -0
  27. data/lib/rails-mcp-server/resources/stimulus_guides_resource.rb +29 -0
  28. data/lib/rails-mcp-server/resources/stimulus_guides_resources.rb +37 -0
  29. data/lib/rails-mcp-server/resources/turbo_guides_resource.rb +29 -0
  30. data/lib/rails-mcp-server/resources/turbo_guides_resources.rb +37 -0
  31. data/lib/rails-mcp-server/tools/analyze_models.rb +1 -1
  32. data/lib/rails-mcp-server/tools/load_guide.rb +370 -0
  33. data/lib/rails-mcp-server/version.rb +1 -1
  34. data/lib/rails_mcp_server.rb +51 -283
  35. metadata +49 -6
@@ -4,6 +4,8 @@ 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"
7
9
  require_relative "rails-mcp-server/utilities/run_process"
8
10
  require_relative "rails-mcp-server/tools/base_tool"
9
11
  require_relative "rails-mcp-server/tools/project_info"
@@ -15,9 +17,29 @@ require_relative "rails-mcp-server/tools/get_schema"
15
17
  require_relative "rails-mcp-server/tools/analyze_controller_views"
16
18
  require_relative "rails-mcp-server/tools/analyze_environment_config"
17
19
  require_relative "rails-mcp-server/tools/switch_project"
20
+ require_relative "rails-mcp-server/tools/load_guide"
21
+ require_relative "rails-mcp-server/resources/base_resource"
22
+
23
+ require_relative "rails-mcp-server/resources/guide_content_formatter"
24
+ require_relative "rails-mcp-server/resources/guide_error_handler"
25
+ require_relative "rails-mcp-server/resources/guide_file_finder"
26
+ require_relative "rails-mcp-server/resources/guide_loader_template"
27
+ require_relative "rails-mcp-server/resources/guide_manifest_operations"
28
+ require_relative "rails-mcp-server/resources/guide_framework_contract"
29
+
30
+ require_relative "rails-mcp-server/resources/rails_guides_resource"
31
+ require_relative "rails-mcp-server/resources/rails_guides_resources"
32
+ require_relative "rails-mcp-server/resources/stimulus_guides_resource"
33
+ require_relative "rails-mcp-server/resources/stimulus_guides_resources"
34
+ require_relative "rails-mcp-server/resources/turbo_guides_resource"
35
+ require_relative "rails-mcp-server/resources/turbo_guides_resources"
36
+ require_relative "rails-mcp-server/resources/custom_guides_resource"
37
+ require_relative "rails-mcp-server/resources/custom_guides_resources"
38
+ require_relative "rails-mcp-server/resources/kamal_guides_resource"
39
+ require_relative "rails-mcp-server/resources/kamal_guides_resources"
18
40
 
19
41
  module RailsMcpServer
20
- @levels = {debug: Logger::DEBUG, info: Logger::INFO, error: Logger::ERROR}
42
+ LEVELS = {debug: Logger::DEBUG, info: Logger::INFO, error: Logger::ERROR}
21
43
  @config = Config.setup
22
44
 
23
45
  class << self
@@ -30,306 +52,52 @@ module RailsMcpServer
30
52
  def_delegators :@config, :projects
31
53
  def_delegators :@config, :current_project, :current_project=
32
54
  def_delegators :@config, :active_project_path, :active_project_path=
55
+ def_delegators :@config, :config_dir
33
56
 
34
57
  def log(level, message)
35
- log_level = @levels[level] || Logger::INFO
58
+ log_level = LEVELS[level] || Logger::INFO
36
59
 
37
60
  @config.logger.add(log_level, message)
38
61
  end
39
- end
40
- class Error < StandardError; end
41
- end
42
-
43
- # rubocop:disable Style/GlobalVars
44
-
45
- # Utility functions for Rails operations
46
- def get_directory_structure(path, max_depth: 3, current_depth: 0, prefix: "")
47
- return "" if current_depth > max_depth || !File.directory?(path)
48
-
49
- # Define ignored directories
50
- ignored_dirs = [
51
- ".git", "node_modules", "tmp", "log",
52
- "storage", "coverage", "public/assets",
53
- "public/packs", ".bundle", "vendor/bundle",
54
- "vendor/cache"
55
- ]
56
-
57
- output = ""
58
- directories = []
59
- files = []
60
-
61
- Dir.foreach(path) do |entry|
62
- next if entry == "." || entry == ".."
63
- next if ignored_dirs.include?(entry) # Skip ignored directories
64
-
65
- full_path = File.join(path, entry)
66
-
67
- if File.directory?(full_path)
68
- directories << entry
69
- else
70
- files << entry
71
- end
72
- end
73
-
74
- directories.sort.each do |dir|
75
- output << "#{prefix}└── #{dir}/\n"
76
- full_path = File.join(path, dir)
77
- output << get_directory_structure(full_path, max_depth: max_depth,
78
- current_depth: current_depth + 1,
79
- prefix: "#{prefix} ")
80
- end
81
-
82
- files.sort.each do |file|
83
- output << "#{prefix}└── #{file}\n"
84
- end
85
-
86
- output
87
- end
88
-
89
- def get_file_extension(path)
90
- case File.extname(path).downcase
91
- when ".rb"
92
- "ruby"
93
- when ".js"
94
- "javascript"
95
- when ".html", ".erb"
96
- "html"
97
- when ".css"
98
- "css"
99
- when ".json"
100
- "json"
101
- when ".yml", ".yaml"
102
- "yaml"
103
- else
104
- ""
105
- end
106
- end
107
-
108
- def execute_rails_command(project_path, command)
109
- full_command = "cd #{project_path} && bin/rails #{command}"
110
- `#{full_command}`
111
- end
112
-
113
- def underscore(string)
114
- string.gsub("::", "/")
115
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
116
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
117
- .tr("-", "_")
118
- .downcase
119
- end
120
-
121
- # Helper method to extract settings from environment files
122
- def extract_env_settings(content)
123
- settings = {}
124
-
125
- # Match configuration settings
126
- content.scan(/config\.([a-zA-Z0-9_.]+)\s*=\s*([^#\n]+)/) do |match|
127
- key = match[0].strip
128
- value = match[1].strip
129
-
130
- # Clean up the value
131
- value = value.chomp(";").strip
132
-
133
- settings[key] = value
134
- end
135
-
136
- settings
137
- end
138
-
139
- # Helper method to find ENV variable usage in the codebase
140
- def find_env_vars_in_codebase(project_path)
141
- env_vars = {}
142
-
143
- # Define directories to search
144
- search_dirs = [
145
- File.join(project_path, "app"),
146
- File.join(project_path, "config"),
147
- File.join(project_path, "lib")
148
- ]
149
-
150
- # Define file patterns to search
151
- file_patterns = ["*.rb", "*.yml", "*.erb", "*.js"]
152
-
153
- search_dirs.each do |dir|
154
- if File.directory?(dir)
155
- file_patterns.each do |pattern|
156
- Dir.glob(File.join(dir, "**", pattern)).each do |file|
157
- content = File.read(file)
158
-
159
- # Extract ENV variables
160
- content.scan(/ENV\s*\[\s*['"]([^'"]+)['"]\s*\]/).each do |match|
161
- env_var = match[0]
162
- env_vars[env_var] ||= []
163
- env_vars[env_var] << file.sub("#{project_path}/", "")
164
- end
165
-
166
- # Also match ENV['VAR'] pattern
167
- content.scan(/ENV\s*\.\s*\[\s*['"]([^'"]+)['"]\s*\]/).each do |match|
168
- env_var = match[0]
169
- env_vars[env_var] ||= []
170
- env_vars[env_var] << file.sub("#{project_path}/", "")
171
- end
172
-
173
- # Also match ENV.fetch('VAR') pattern
174
- content.scan(/ENV\s*\.\s*fetch\s*\(\s*['"]([^'"]+)['"]\s*/).each do |match|
175
- env_var = match[0]
176
- env_vars[env_var] ||= []
177
- env_vars[env_var] << file.sub("#{project_path}/", "")
178
- end
179
- rescue => e
180
- log(:error, "Error reading file #{file}: #{e.message}")
181
- end
182
- end
183
- end
184
- end
185
-
186
- env_vars
187
- end
188
-
189
- # Helper method to parse .env files
190
- def parse_dotenv_file(file_path)
191
- vars = {}
192
62
 
193
- begin
194
- File.readlines(file_path).each do |line| # rubocop:disable Performance/IoReadlines
195
- # Skip comments and empty lines
196
- next if line.strip.empty? || line.strip.start_with?("#")
197
-
198
- # Parse KEY=value pattern
199
- if line =~ /\A([A-Za-z0-9_]+)=(.*)\z/
200
- key = $1
201
- # Store just the existence of the variable, not its value
202
- vars[key] = true
203
- end
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!
204
68
  end
205
- rescue => e
206
- log(:error, "Error parsing .env file #{file_path}: #{e.message}")
207
- end
208
-
209
- vars
210
- end
211
-
212
- # Helper method to parse database.yml
213
- def parse_database_config(file_path)
214
- config = {}
215
69
 
216
- begin
217
- # Simple YAML parsing - not handling ERB
218
- yaml_content = File.read(file_path)
219
- yaml_data = YAML.safe_load(yaml_content) || {}
220
-
221
- # Extract environment configurations
222
- %w[development test production staging].each do |env|
223
- config[env] = yaml_data[env] if yaml_data[env]
70
+ # Check if resource extensions are loaded
71
+ def resource_extensions_loaded?
72
+ Extensions::ResourceExtensionSetup.setup_complete?
224
73
  end
225
- rescue => e
226
- log(:error, "Error parsing database.yml: #{e.message}")
227
- end
228
-
229
- config
230
- end
231
-
232
- # Helper method to compare environment settings
233
- def compare_environment_settings(env_settings)
234
- result = {
235
- unique_settings: {},
236
- different_values: {}
237
- }
238
74
 
239
- # Get all settings across all environments
240
- all_settings = env_settings.values.map(&:keys).flatten.uniq # rubocop:disable Performance/ChainArrayAllocation
241
-
242
- # Find settings unique to certain environments
243
- env_settings.each do |env, settings|
244
- unique = settings.keys - (all_settings - settings.keys)
245
- result[:unique_settings][env] = unique if unique.any?
246
- end
247
-
248
- # Find settings with different values across environments
249
- all_settings.each do |setting|
250
- values = {}
251
-
252
- env_settings.each do |env, settings|
253
- values[env] = settings[setting] if settings[setting]
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!
254
79
  end
255
80
 
256
- # Only include if there are different values
257
- if values.values.uniq.size > 1
258
- result[:different_values][setting] = values
81
+ # Check if server extensions are loaded
82
+ def server_extensions_loaded?
83
+ Extensions::ServerExtensionSetup.setup_complete?
259
84
  end
260
- end
261
85
 
262
- result
263
- end
264
-
265
- # Helper method to find missing ENV variables
266
- def find_missing_env_vars(env_vars_in_code, dotenv_vars)
267
- missing_vars = {}
268
-
269
- # Check each ENV variable used in code
270
- env_vars_in_code.each do |var, files|
271
- # Environments where the variable is missing
272
- missing_in = []
273
-
274
- # Check in each .env file
275
- if dotenv_vars.empty?
276
- missing_in << "all environments (no .env files found)"
277
- else
278
- dotenv_vars.each do |env_file, vars|
279
- env_name = env_file.gsub(/^\.env\.?|\.local$/, "")
280
- env_name = "development" if env_name.empty?
281
-
282
- if !vars.key?(var)
283
- missing_in << env_name
284
- end
285
- end
86
+ # Setup all extensions at once
87
+ def setup_extensions!
88
+ setup_resource_extensions!
89
+ # Server extensions are setup automatically by resource extensions
286
90
  end
287
91
 
288
- missing_vars[var] = missing_in if missing_in.any?
289
- end
290
-
291
- missing_vars
292
- end
293
-
294
- # Helper method to check for security issues
295
- def check_security_configuration(env_settings, database_config)
296
- findings = []
297
-
298
- # Check for common security settings
299
- env_settings.each do |env, settings|
300
- # Check for secure cookies in production
301
- if env == "production"
302
- if settings["cookies.secure"] == "false"
303
- findings << "Production has cookies.secure = false"
304
- end
305
-
306
- if settings["session_store.secure"] == "false"
307
- findings << "Production has session_store.secure = false"
308
- end
309
-
310
- # Force SSL
311
- if settings["force_ssl"] == "false"
312
- findings << "Production has force_ssl = false"
313
- end
314
- end
315
-
316
- # Check for CSRF protection
317
- if settings["action_controller.default_protect_from_forgery"] == "false"
318
- findings << "#{env} has CSRF protection disabled"
92
+ # Check if all extensions are loaded
93
+ def extensions_loaded?
94
+ resource_extensions_loaded? && server_extensions_loaded?
319
95
  end
320
96
  end
321
97
 
322
- # Check for hardcoded credentials in database.yml
323
- database_config.each do |env, config|
324
- if config["username"] && !config["username"].include?("ENV")
325
- findings << "Database username hardcoded in database.yml for #{env}"
326
- end
327
-
328
- if config["password"] && !config["password"].include?("ENV")
329
- findings << "Database password hardcoded in database.yml for #{env}"
330
- end
331
- end
98
+ class Error < StandardError; end
332
99
 
333
- findings
100
+ # Auto-setup extensions when the module is loaded
101
+ # This ensures extensions are available immediately
102
+ setup_resource_extensions!
334
103
  end
335
- # rubocop:enable Style/GlobalVars
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.1.4
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Alberto Chávez Cárdenas
@@ -9,20 +9,34 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: addressable
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.8'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: fast-mcp
14
28
  requirement: !ruby/object:Gem::Requirement
15
29
  requirements:
16
30
  - - "~>"
17
31
  - !ruby/object:Gem::Version
18
- version: 1.1.0
32
+ version: 1.4.0
19
33
  type: :runtime
20
34
  prerelease: false
21
35
  version_requirements: !ruby/object:Gem::Requirement
22
36
  requirements:
23
37
  - - "~>"
24
38
  - !ruby/object:Gem::Version
25
- version: 1.1.0
39
+ version: 1.4.0
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: rack
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -57,14 +71,14 @@ dependencies:
57
71
  requirements:
58
72
  - - "~>"
59
73
  - !ruby/object:Gem::Version
60
- version: 1.6.6
74
+ version: 1.7.0
61
75
  type: :runtime
62
76
  prerelease: false
63
77
  version_requirements: !ruby/object:Gem::Requirement
64
78
  requirements:
65
79
  - - "~>"
66
80
  - !ruby/object:Gem::Version
67
- version: 1.6.6
81
+ version: 1.7.0
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: standard
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -84,15 +98,42 @@ email:
84
98
  - mario.chavez@gmail.com
85
99
  executables:
86
100
  - rails-mcp-server
101
+ - rails-mcp-server-download-resources
87
102
  - rails-mcp-setup-claude
88
103
  extensions: []
89
104
  extra_rdoc_files: []
90
105
  files:
106
+ - CHANGELOG.md
91
107
  - LICENSE.txt
92
108
  - README.md
109
+ - config/resources.yml
110
+ - docs/RESOURCES.md
93
111
  - exe/rails-mcp-server
112
+ - exe/rails-mcp-server-download-resources
94
113
  - exe/rails-mcp-setup-claude
95
114
  - 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
+ - lib/rails-mcp-server/helpers/resource_base.rb
118
+ - lib/rails-mcp-server/helpers/resource_downloader.rb
119
+ - lib/rails-mcp-server/helpers/resource_importer.rb
120
+ - lib/rails-mcp-server/resources/base_resource.rb
121
+ - lib/rails-mcp-server/resources/custom_guides_resource.rb
122
+ - lib/rails-mcp-server/resources/custom_guides_resources.rb
123
+ - lib/rails-mcp-server/resources/guide_content_formatter.rb
124
+ - lib/rails-mcp-server/resources/guide_error_handler.rb
125
+ - lib/rails-mcp-server/resources/guide_file_finder.rb
126
+ - lib/rails-mcp-server/resources/guide_framework_contract.rb
127
+ - lib/rails-mcp-server/resources/guide_loader_template.rb
128
+ - lib/rails-mcp-server/resources/guide_manifest_operations.rb
129
+ - lib/rails-mcp-server/resources/kamal_guides_resource.rb
130
+ - lib/rails-mcp-server/resources/kamal_guides_resources.rb
131
+ - lib/rails-mcp-server/resources/rails_guides_resource.rb
132
+ - lib/rails-mcp-server/resources/rails_guides_resources.rb
133
+ - lib/rails-mcp-server/resources/stimulus_guides_resource.rb
134
+ - lib/rails-mcp-server/resources/stimulus_guides_resources.rb
135
+ - lib/rails-mcp-server/resources/turbo_guides_resource.rb
136
+ - lib/rails-mcp-server/resources/turbo_guides_resources.rb
96
137
  - lib/rails-mcp-server/tools/analyze_controller_views.rb
97
138
  - lib/rails-mcp-server/tools/analyze_environment_config.rb
98
139
  - lib/rails-mcp-server/tools/analyze_models.rb
@@ -102,6 +143,7 @@ files:
102
143
  - lib/rails-mcp-server/tools/get_routes.rb
103
144
  - lib/rails-mcp-server/tools/get_schema.rb
104
145
  - lib/rails-mcp-server/tools/list_files.rb
146
+ - lib/rails-mcp-server/tools/load_guide.rb
105
147
  - lib/rails-mcp-server/tools/project_info.rb
106
148
  - lib/rails-mcp-server/tools/switch_project.rb
107
149
  - lib/rails-mcp-server/utilities/run_process.rb
@@ -114,6 +156,7 @@ metadata:
114
156
  allowed_push_host: https://rubygems.org
115
157
  homepage_uri: https://github.com/maquina-app/rails-mcp-server
116
158
  source_code_uri: https://github.com/maquina-app/rails-mcp-server
159
+ changelog_uri: https://github.com/maquina-app/rails-mcp-server/blob/main/CHANGELOG.md
117
160
  rdoc_options: []
118
161
  require_paths:
119
162
  - lib
@@ -128,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
171
  - !ruby/object:Gem::Version
129
172
  version: '0'
130
173
  requirements: []
131
- rubygems_version: 3.6.8
174
+ rubygems_version: 3.6.9
132
175
  specification_version: 4
133
176
  summary: MCP server for Rails projects
134
177
  test_files: []