rails-mcp-server 1.0.1 → 1.1.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.
@@ -0,0 +1,116 @@
1
+ module RailsMcpServer
2
+ class AnalyzeModels < BaseTool
3
+ tool_name "analize_models"
4
+
5
+ description "Retrieve detailed information about Active Record models in the project. When called without parameters, lists all model files. When a specific model is specified, returns its schema, associations (has_many, belongs_to, has_one), and complete source code."
6
+
7
+ arguments do
8
+ optional(:model_name).filled(:string).description("Class name of a specific model to get detailed information for (e.g., 'User', 'Product'). Use CamelCase, not snake_case. If omitted, returns a list of all models.")
9
+ end
10
+
11
+ def call(model_name: nil)
12
+ unless current_project
13
+ message = "No active project. Please switch to a project first."
14
+ log(:warn, message)
15
+
16
+ return message
17
+ end
18
+
19
+ if model_name
20
+ log(:info, "Getting info for specific model: #{model_name}")
21
+
22
+ # Check if the model file exists
23
+ model_file = File.join(active_project_path, "app", "models", "#{underscore(model_name)}.rb")
24
+ unless File.exist?(model_file)
25
+ log(:warn, "Model file not found: #{model_name}")
26
+ message = "Model '#{model_name}' not found."
27
+ log(:warn, message)
28
+
29
+ return message
30
+ end
31
+
32
+ log(:debug, "Reading model file: #{model_file}")
33
+
34
+ # Get the model file content
35
+ model_content = File.read(model_file)
36
+
37
+ # Try to get schema information
38
+ log(:debug, "Executing Rails runner to get schema information")
39
+ schema_info = execute_rails_command(
40
+ active_project_path,
41
+ "runner \"puts #{model_name}.column_names\""
42
+ )
43
+
44
+ # Try to get associations
45
+ associations = []
46
+ if model_content.include?("has_many")
47
+ has_many = model_content.scan(/has_many\s+:(\w+)/).flatten
48
+ associations << "Has many: #{has_many.join(", ")}" unless has_many.empty?
49
+ end
50
+
51
+ if model_content.include?("belongs_to")
52
+ belongs_to = model_content.scan(/belongs_to\s+:(\w+)/).flatten
53
+ associations << "Belongs to: #{belongs_to.join(", ")}" unless belongs_to.empty?
54
+ end
55
+
56
+ if model_content.include?("has_one")
57
+ has_one = model_content.scan(/has_one\s+:(\w+)/).flatten
58
+ associations << "Has one: #{has_one.join(", ")}" unless has_one.empty?
59
+ end
60
+
61
+ log(:debug, "Found #{associations.size} associations for model: #{model_name}")
62
+
63
+ # Format the output
64
+ <<~INFO
65
+ Model: #{model_name}
66
+
67
+ Schema:
68
+ #{schema_info}
69
+
70
+ Associations:
71
+ #{associations.empty? ? "None found" : associations.join("\n")}
72
+
73
+ Model Definition:
74
+ ```ruby
75
+ #{model_content}
76
+ ```
77
+ INFO
78
+ else
79
+ log(:info, "Listing all models")
80
+
81
+ # List all models
82
+ models_dir = File.join(active_project_path, "app", "models")
83
+ unless File.directory?(models_dir)
84
+ message = "Models directory not found."
85
+ log(:warn, message)
86
+
87
+ return message
88
+ end
89
+
90
+ # Get all .rb files in the models directory and its subdirectories
91
+ model_files = Dir.glob(File.join(models_dir, "**", "*.rb"))
92
+ .map { |f| f.sub("#{models_dir}/", "").sub(/\.rb$/, "") }
93
+ .sort # rubocop:disable Performance/ChainArrayAllocation
94
+
95
+ log(:debug, "Found #{model_files.size} model files")
96
+
97
+ "Models in the project:\n\n#{model_files.join("\n")}"
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def execute_rails_command(project_path, command)
104
+ full_command = "cd #{project_path} && bin/rails #{command}"
105
+ `#{full_command}`
106
+ end
107
+
108
+ def underscore(string)
109
+ string.gsub("::", "/")
110
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
111
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
112
+ .tr("-", "_")
113
+ .downcase
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,9 @@
1
+ module RailsMcpServer
2
+ class BaseTool < FastMcp::Tool
3
+ extend Forwardable
4
+
5
+ def_delegators :RailsMcpServer, :log, :projects
6
+ def_delegators :RailsMcpServer, :current_project=, :current_project
7
+ def_delegators :RailsMcpServer, :active_project_path=, :active_project_path
8
+ end
9
+ end
@@ -0,0 +1,55 @@
1
+ module RailsMcpServer
2
+ class GetFile < BaseTool
3
+ tool_name "get_file"
4
+
5
+ description "Retrieve the complete content of a specific file with syntax highlighting. Use this to examine implementation details, configurations, or any text file in the project."
6
+
7
+ arguments do
8
+ required(:path).filled(:string).description("File path relative to the project root (e.g., 'app/models/user.rb', 'config/routes.rb'). Use list_files first if you're not sure about the exact path.")
9
+ end
10
+
11
+ def call(path:)
12
+ unless current_project
13
+ message = "No active project. Please switch to a project first."
14
+ log(:warn, message)
15
+
16
+ return message
17
+ end
18
+
19
+ full_path = File.join(active_project_path, path)
20
+
21
+ unless File.exist?(full_path)
22
+ message = "File '#{path}' not found in the project."
23
+ log(:warn, message)
24
+
25
+ return message
26
+ end
27
+
28
+ content = File.read(full_path)
29
+ log(:debug, "Read file: #{path} (#{content.size} bytes)")
30
+
31
+ "File: #{path}\n\n```#{get_file_extension(path)}\n#{content}\n```"
32
+ end
33
+
34
+ private
35
+
36
+ def get_file_extension(path)
37
+ case File.extname(path).downcase
38
+ when ".rb"
39
+ "ruby"
40
+ when ".js"
41
+ "javascript"
42
+ when ".html", ".erb"
43
+ "html"
44
+ when ".css"
45
+ "css"
46
+ when ".json"
47
+ "json"
48
+ when ".yml", ".yaml"
49
+ "yaml"
50
+ else
51
+ ""
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,116 @@
1
+ module RailsMcpServer
2
+ class GetModels < BaseTool
3
+ tool_name "get_models"
4
+
5
+ description "Retrieve detailed information about Active Record models in the project. When called without parameters, lists all model files. When a specific model is specified, returns its schema, associations (has_many, belongs_to, has_one), and complete source code."
6
+
7
+ arguments do
8
+ optional(:model_name).filled(:string).description("Class name of a specific model to get detailed information for (e.g., 'User', 'Product'). Use CamelCase, not snake_case. If omitted, returns a list of all models.")
9
+ end
10
+
11
+ def call(model_name: nil)
12
+ unless current_project
13
+ message = "No active project. Please switch to a project first."
14
+ log(:warn, message)
15
+
16
+ return message
17
+ end
18
+
19
+ if model_name
20
+ log(:info, "Getting info for specific model: #{model_name}")
21
+
22
+ # Check if the model file exists
23
+ model_file = File.join(active_project_path, "app", "models", "#{underscore(model_name)}.rb")
24
+ unless File.exist?(model_file)
25
+ log(:warn, "Model file not found: #{model_name}")
26
+ message = "Model '#{model_name}' not found."
27
+ log(:warn, message)
28
+
29
+ return message
30
+ end
31
+
32
+ log(:debug, "Reading model file: #{model_file}")
33
+
34
+ # Get the model file content
35
+ model_content = File.read(model_file)
36
+
37
+ # Try to get schema information
38
+ log(:debug, "Executing Rails runner to get schema information")
39
+ schema_info = execute_rails_command(
40
+ active_project_path,
41
+ "runner \"puts #{model_name}.column_names\""
42
+ )
43
+
44
+ # Try to get associations
45
+ associations = []
46
+ if model_content.include?("has_many")
47
+ has_many = model_content.scan(/has_many\s+:(\w+)/).flatten
48
+ associations << "Has many: #{has_many.join(", ")}" unless has_many.empty?
49
+ end
50
+
51
+ if model_content.include?("belongs_to")
52
+ belongs_to = model_content.scan(/belongs_to\s+:(\w+)/).flatten
53
+ associations << "Belongs to: #{belongs_to.join(", ")}" unless belongs_to.empty?
54
+ end
55
+
56
+ if model_content.include?("has_one")
57
+ has_one = model_content.scan(/has_one\s+:(\w+)/).flatten
58
+ associations << "Has one: #{has_one.join(", ")}" unless has_one.empty?
59
+ end
60
+
61
+ log(:debug, "Found #{associations.size} associations for model: #{model_name}")
62
+
63
+ # Format the output
64
+ <<~INFO
65
+ Model: #{model_name}
66
+
67
+ Schema:
68
+ #{schema_info}
69
+
70
+ Associations:
71
+ #{associations.empty? ? "None found" : associations.join("\n")}
72
+
73
+ Model Definition:
74
+ ```ruby
75
+ #{model_content}
76
+ ```
77
+ INFO
78
+ else
79
+ log(:info, "Listing all models")
80
+
81
+ # List all models
82
+ models_dir = File.join(active_project_path, "app", "models")
83
+ unless File.directory?(models_dir)
84
+ message = "Models directory not found."
85
+ log(:warn, message)
86
+
87
+ return message
88
+ end
89
+
90
+ # Get all .rb files in the models directory and its subdirectories
91
+ model_files = Dir.glob(File.join(models_dir, "**", "*.rb"))
92
+ .map { |f| f.sub("#{models_dir}/", "").sub(/\.rb$/, "") }
93
+ .sort # rubocop:disable Performance/ChainArrayAllocation
94
+
95
+ log(:debug, "Found #{model_files.size} model files")
96
+
97
+ "Models in the project:\n\n#{model_files.join("\n")}"
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def execute_rails_command(project_path, command)
104
+ full_command = "cd #{project_path} && bin/rails #{command}"
105
+ `#{full_command}`
106
+ end
107
+
108
+ def underscore(string)
109
+ string.gsub("::", "/")
110
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
111
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
112
+ .tr("-", "_")
113
+ .downcase
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,24 @@
1
+ module RailsMcpServer
2
+ class GetRoutes < BaseTool
3
+ tool_name "get_routes"
4
+
5
+ description "Retrieve all HTTP routes defined in the Rails application with their associated controllers and actions. Equivalent to running 'rails routes' command. This helps understand the API endpoints or page URLs available in the application."
6
+
7
+ def call
8
+ unless current_project
9
+ message = "No active project. Please switch to a project first."
10
+ log(:warn, message)
11
+
12
+ return message
13
+ end
14
+
15
+ # Execute the Rails routes command
16
+ routes_output = RailsMcpServer::RunProcess.execute_rails_command(
17
+ active_project_path, "bin/rails routes"
18
+ )
19
+ log(:debug, "Routes command completed, output size: #{routes_output.size} bytes")
20
+
21
+ "Rails Routes:\n\n```\n#{routes_output}\n```"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,141 @@
1
+ module RailsMcpServer
2
+ class GetSchema < BaseTool
3
+ tool_name "get_schema"
4
+
5
+ description "Retrieve database schema information for the Rails application. Without parameters, returns all tables and the complete schema.rb. With a table name, returns detailed column information including data types, constraints, and foreign keys for that specific table."
6
+
7
+ arguments do
8
+ optional(:table_name).filled(:string).description("Database table name to get detailed schema information for (e.g., 'users', 'products'). Use snake_case, plural form. If omitted, returns complete database schema.")
9
+ end
10
+
11
+ def call(table_name: nil)
12
+ unless current_project
13
+ message = "No active project. Please switch to a project first."
14
+ log(:warn, message)
15
+
16
+ return message
17
+ end
18
+
19
+ if table_name
20
+ log(:info, "Getting schema for table: #{table_name}")
21
+
22
+ # Execute the Rails schema command for a specific table
23
+ schema_output = RailsMcpServer::RunProcess.execute_rails_command(
24
+ active_project_path,
25
+ "bin/rails runner \"require 'active_record'; puts ActiveRecord::Base.connection.columns('#{table_name}').map{|c| [c.name, c.type, c.null, c.default].inspect}.join('\n')\""
26
+ )
27
+
28
+ if schema_output.strip.empty?
29
+ message = "Table '#{table_name}' not found or has no columns."
30
+ log(:warn, message)
31
+
32
+ return message
33
+ end
34
+
35
+ # Parse the column information
36
+ columns = schema_output.strip.split("\n").map do |column_info|
37
+ eval(column_info) # This is safe because we're generating the string ourselves # rubocop:disable Security/Eval
38
+ end
39
+
40
+ # Format the output
41
+ formatted_columns = columns.map do |name, type, nullable, default|
42
+ "#{name} (#{type})#{nullable ? ", nullable" : ""}#{default ? ", default: #{default}" : ""}"
43
+ end
44
+
45
+ output = <<~SCHEMA
46
+ Table: #{table_name}
47
+
48
+ Columns:
49
+ #{formatted_columns.join("\n")}
50
+ SCHEMA
51
+
52
+ # Try to get foreign keys
53
+ begin
54
+ fk_output = RailsMcpServer::RunProcess.execute_rails_command(
55
+ active_project_path,
56
+ "bin/rails runner \"require 'active_record'; puts ActiveRecord::Base.connection.foreign_keys('#{table_name}').map{|fk| [fk.from_table, fk.to_table, fk.column, fk.primary_key].inspect}.join('\n')\""
57
+ )
58
+
59
+ unless fk_output.strip.empty?
60
+ foreign_keys = fk_output.strip.split("\n").map do |fk_info|
61
+ eval(fk_info) # This is safe because we're generating the string ourselves # rubocop:disable Security/Eval
62
+ end
63
+
64
+ formatted_fks = foreign_keys.map do |from_table, to_table, column, primary_key|
65
+ "#{column} -> #{to_table}.#{primary_key}"
66
+ end
67
+
68
+ output += <<~FK
69
+
70
+ Foreign Keys:
71
+ #{formatted_fks.join("\n")}
72
+ FK
73
+ end
74
+ rescue => e
75
+ log(:warn, "Error fetching foreign keys: #{e.message}")
76
+ end
77
+
78
+ output
79
+ else
80
+ log(:info, "Getting full schema")
81
+
82
+ # Execute the Rails schema:dump command
83
+ # First, check if we need to create the schema file
84
+ schema_file = File.join(active_project_path, "db", "schema.rb")
85
+ unless File.exist?(schema_file)
86
+ log(:info, "Schema file not found, attempting to generate it")
87
+ RailsMcpServer::RunProcess.execute_rails_command(active_project_path, "db:schema:dump")
88
+ end
89
+
90
+ if File.exist?(schema_file)
91
+ # Read the schema file
92
+ schema_content = File.read(schema_file)
93
+
94
+ # Try to get table list
95
+ tables_output = RailsMcpServer::RunProcess.execute_rails_command(
96
+ active_project_path,
97
+ "bin/rails runner \"require 'active_record'; puts ActiveRecord::Base.connection.tables.sort.join('\n')\""
98
+ )
99
+
100
+ tables = tables_output.strip.split("\n")
101
+
102
+ <<~SCHEMA
103
+ Database Schema
104
+
105
+ Tables:
106
+ #{tables.join("\n")}
107
+
108
+ Schema Definition:
109
+ ```ruby
110
+ #{schema_content}
111
+ ```
112
+ SCHEMA
113
+ else
114
+ # If we can't get the schema file, try to get the table list
115
+ tables_output = RailsMcpServer::RunProcess.execute_rails_command(
116
+ active_project_path,
117
+ "bin/rails runner \"require 'active_record'; puts ActiveRecord::Base.connection.tables.sort.join('\n')\""
118
+ )
119
+
120
+ if tables_output.strip.empty?
121
+ message = "Could not retrieve schema information. Try running 'rails db:schema:dump' in your project first."
122
+ log(:warn, message)
123
+
124
+ return message
125
+ end
126
+
127
+ tables = tables_output.strip.split("\n")
128
+
129
+ <<~SCHEMA
130
+ Database Schema
131
+
132
+ Tables:
133
+ #{tables.join("\n")}
134
+
135
+ Note: Full schema definition is not available. Run 'rails db:schema:dump' to generate the schema.rb file.
136
+ SCHEMA
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,54 @@
1
+ module RailsMcpServer
2
+ class ListFiles < BaseTool
3
+ tool_name "list_files"
4
+
5
+ description "List files in the Rails project matching specific criteria. Use this to explore project directories or locate specific file types. If no parameters are provided, lists files in the project root."
6
+
7
+ arguments do
8
+ optional(:directory).filled(:string).description("Directory path relative to the project root (e.g., 'app/models', 'config'). Leave empty to list files at the root.")
9
+ optional(:pattern).filled(:string).description("File pattern using glob syntax (e.g., '*.rb' for Ruby files, '*.erb' for ERB templates, '*_controller.rb' for controllers)")
10
+ end
11
+
12
+ def call(directory: "", pattern: "*.rb")
13
+ unless current_project
14
+ message = "No active project. Please switch to a project first."
15
+ log(:warn, message)
16
+
17
+ return message
18
+ end
19
+
20
+ full_path = File.join(active_project_path, directory)
21
+ unless File.directory?(full_path)
22
+ message = "Directory '#{directory}' not found in the project."
23
+ log(:warn, message)
24
+
25
+ return message
26
+ end
27
+
28
+ # Check if this is a git repository
29
+ is_git_repo = system("cd #{active_project_path} && git rev-parse --is-inside-work-tree > /dev/null 2>&1")
30
+
31
+ if is_git_repo
32
+ log(:debug, "Project is a git repository, using git ls-files")
33
+
34
+ # Use git ls-files for tracked files
35
+ relative_dir = directory.empty? ? "" : "#{directory}/"
36
+ git_cmd = "cd #{active_project_path} && git ls-files --cached --others --exclude-standard #{relative_dir}#{pattern}"
37
+
38
+ files = `#{git_cmd}`.split("\n").map(&:strip).sort # rubocop:disable Performance/ChainArrayAllocation
39
+ else
40
+ log(:debug, "Project is not a git repository or git not available, using Dir.glob")
41
+
42
+ # Use Dir.glob as fallback
43
+ files = Dir.glob(File.join(full_path, pattern))
44
+ .map { |f| f.sub("#{active_project_path}/", "") }
45
+ .reject { |file| file.start_with?(".git/", ".ruby-lsp/", "node_modules/", "storage/", "public/assets/", "public/packs/", ".bundle/", "vendor/bundle/", "vendor/cache/", "tmp/", "log/") } # rubocop:disable Performance/ChainArrayAllocation
46
+ .sort # rubocop:disable Performance/ChainArrayAllocation
47
+ end
48
+
49
+ log(:debug, "Found #{files.size} files matching pattern (respecting .gitignore and ignoring node_modules)")
50
+
51
+ "Files in #{directory.empty? ? "project root" : directory} matching '#{pattern}':\n\n#{files.join("\n")}"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,86 @@
1
+ module RailsMcpServer
2
+ class ProjectInfo < BaseTool
3
+ tool_name "project_info"
4
+
5
+ description "Retrieve comprehensive information about the current Rails project, including Rails version, directory structure, API-only status, and overall project organization. Useful for initial project exploration and understanding the codebase structure."
6
+
7
+ def call
8
+ unless current_project
9
+ message = "No active project. Please switch to a project first."
10
+ log(:warn, message)
11
+
12
+ return message
13
+ end
14
+
15
+ # Get additional project information
16
+ gemfile_path = File.join(active_project_path, "Gemfile")
17
+ gemfile_content = File.exist?(gemfile_path) ? File.read(gemfile_path) : "Gemfile not found"
18
+
19
+ # Get Rails version
20
+ rails_version = gemfile_content.match(/gem ['"]rails['"],\s*['"](.+?)['"]/)&.captures&.first || "Unknown"
21
+
22
+ # Check if it's an API-only app
23
+ config_application_path = File.join(active_project_path, "config", "application.rb")
24
+ is_api_only = File.exist?(config_application_path) &&
25
+ File.read(config_application_path).include?("config.api_only = true")
26
+
27
+ log(:info, "Project info: Rails v#{rails_version}, API-only: #{is_api_only}")
28
+
29
+ <<~INFO
30
+ Current project: #{current_project}
31
+ Path: #{active_project_path}
32
+ Rails version: #{rails_version}
33
+ API only: #{is_api_only ? "Yes" : "No"}
34
+
35
+ Project structure:
36
+ #{get_directory_structure(active_project_path, max_depth: 2)}
37
+ INFO
38
+ end
39
+
40
+ private
41
+
42
+ # Utility functions for Rails operations
43
+ def get_directory_structure(path, max_depth: 3, current_depth: 0, prefix: "")
44
+ return "" if current_depth > max_depth || !File.directory?(path)
45
+
46
+ # Define ignored directories
47
+ ignored_dirs = [
48
+ ".git", "node_modules", "tmp", "log",
49
+ "storage", "coverage", "public/assets",
50
+ "public/packs", ".bundle", "vendor/bundle",
51
+ "vendor/cache", ".ruby-lsp"
52
+ ]
53
+
54
+ output = ""
55
+ directories = []
56
+ files = []
57
+
58
+ Dir.foreach(path) do |entry|
59
+ next if entry == "." || entry == ".."
60
+ next if ignored_dirs.include?(entry) # Skip ignored directories
61
+
62
+ full_path = File.join(path, entry)
63
+
64
+ if File.directory?(full_path)
65
+ directories << entry
66
+ else
67
+ files << entry
68
+ end
69
+ end
70
+
71
+ directories.sort.each do |dir|
72
+ output << "#{prefix}└── #{dir}/\n"
73
+ full_path = File.join(path, dir)
74
+ output << get_directory_structure(full_path, max_depth: max_depth,
75
+ current_depth: current_depth + 1,
76
+ prefix: "#{prefix} ")
77
+ end
78
+
79
+ files.sort.each do |file|
80
+ output << "#{prefix}└── #{file}\n"
81
+ end
82
+
83
+ output
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,25 @@
1
+ module RailsMcpServer
2
+ class SwitchProject < BaseTool
3
+ tool_name "switch_project"
4
+
5
+ description "Change the active Rails project to interact with a different codebase. Must be called before using other tools. Available projects are defined in the projects.yml configuration file."
6
+
7
+ arguments do
8
+ required(:project_name).filled(:string).description("Name of the project as defined in the projects.yml file (case-sensitive)")
9
+ end
10
+
11
+ def call(project_name:)
12
+ if projects.key?(project_name)
13
+ self.current_project = project_name
14
+ self.active_project_path = File.expand_path(projects[project_name])
15
+ log(:info, "Switched to project: #{project_name} at path: #{active_project_path}")
16
+
17
+ "Switched to project: #{project_name} at path: #{active_project_path}"
18
+ else
19
+ log(:warn, "Project not found: #{project_name}")
20
+
21
+ "Project '#{project_name}' not found. Available projects: #{projects.keys.join(", ")}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ module RailsMcpServer
2
+ class RunProcess
3
+ def self.execute_rails_command(project_path, command)
4
+ subprocess_env = ENV.to_h.merge(Bundler.original_env).merge(
5
+ "BUNDLE_GEMFILE" => File.join(project_path, "Gemfile")
6
+ )
7
+
8
+ RailsMcpServer.log(:debug, "Executing: #{command}")
9
+
10
+ # Execute the command and capture stdout, stderr, and status
11
+ stdout_str, stderr_str, status = Open3.capture3(subprocess_env, command, chdir: project_path)
12
+
13
+ if status.success?
14
+ RailsMcpServer.log(:debug, "Command succeeded")
15
+ stdout_str
16
+ else
17
+ # Log error details
18
+ RailsMcpServer.log(:error, "Command failed with status: #{status.exitstatus}")
19
+ RailsMcpServer.log(:error, "stderr: #{stderr_str}")
20
+
21
+ # Return error message
22
+ "Error executing Rails command: #{command}\n\n#{stderr_str}"
23
+ end
24
+ rescue => e
25
+ RailsMcpServer.log(:error, "Exception executing Rails command: #{e.message}")
26
+ "Exception executing command: #{e.message}"
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsMcpServer
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end