rails-active-mcp 0.1.7 → 2.0.7

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -279
  3. data/changelog.md +69 -0
  4. data/docs/DEBUGGING.md +5 -5
  5. data/docs/README.md +130 -142
  6. data/exe/rails-active-mcp-server +153 -76
  7. data/lib/generators/rails_active_mcp/install/install_generator.rb +19 -39
  8. data/lib/generators/rails_active_mcp/install/templates/README.md +30 -164
  9. data/lib/generators/rails_active_mcp/install/templates/initializer.rb +37 -38
  10. data/lib/generators/rails_active_mcp/install/templates/mcp.ru +7 -3
  11. data/lib/rails_active_mcp/configuration.rb +37 -98
  12. data/lib/rails_active_mcp/console_executor.rb +13 -3
  13. data/lib/rails_active_mcp/engine.rb +36 -24
  14. data/lib/rails_active_mcp/sdk/server.rb +183 -0
  15. data/lib/rails_active_mcp/sdk/tools/console_execute_tool.rb +103 -0
  16. data/lib/rails_active_mcp/sdk/tools/dry_run_tool.rb +73 -0
  17. data/lib/rails_active_mcp/sdk/tools/model_info_tool.rb +106 -0
  18. data/lib/rails_active_mcp/sdk/tools/safe_query_tool.rb +77 -0
  19. data/lib/rails_active_mcp/version.rb +1 -1
  20. data/lib/rails_active_mcp.rb +5 -11
  21. data/rails_active_mcp.gemspec +4 -1
  22. metadata +22 -11
  23. data/app/controllers/rails_active_mcp/mcp_controller.rb +0 -80
  24. data/lib/rails_active_mcp/mcp_server.rb +0 -383
  25. data/lib/rails_active_mcp/railtie.rb +0 -70
  26. data/lib/rails_active_mcp/stdio_server.rb +0 -517
  27. data/lib/rails_active_mcp/tools/console_execute_tool.rb +0 -61
  28. data/lib/rails_active_mcp/tools/dry_run_tool.rb +0 -41
  29. data/lib/rails_active_mcp/tools/model_info_tool.rb +0 -70
  30. data/lib/rails_active_mcp/tools/safe_query_tool.rb +0 -41
@@ -20,30 +20,24 @@ module RailsActiveMcp
20
20
  # This ensures the server runs within the Rails project context
21
21
 
22
22
  require 'bundler/setup'
23
- require 'stringio'
24
23
 
25
24
  # Set Rails environment
26
25
  ENV['RAILS_ENV'] ||= 'development'
27
26
 
28
- # Suppress Rails loading output for MCP JSON protocol
29
- unless ENV['RAILS_MCP_DEBUG'] == '1'
30
- original_stdout = $stdout
31
- original_stderr = $stderr
32
- $stdout = StringIO.new
33
- $stderr = StringIO.new
34
- end
35
-
36
- # Load Rails application
27
+ # Load Rails application first
37
28
  require_relative '../config/environment'
38
29
 
39
- # Restore output streams
40
- unless ENV['RAILS_MCP_DEBUG'] == '1'
41
- $stdout = original_stdout
42
- $stderr = original_stderr
43
- end
30
+ # Now start the MCP server using the SDK implementation
31
+ require 'rails_active_mcp/sdk/server'
44
32
 
45
- # Now run the actual MCP server
46
- load Gem.bin_path('rails_active_mcp', 'rails-active-mcp-server')
33
+ begin
34
+ server = RailsActiveMcp::SDK::Server.new
35
+ server.run
36
+ rescue StandardError => e
37
+ warn "Error starting Rails Active MCP server: \#{e.message}"
38
+ warn e.backtrace if ENV['RAILS_MCP_DEBUG'] == '1'
39
+ exit 1
40
+ end
47
41
  RUBY
48
42
 
49
43
  chmod 'bin/rails-active-mcp-server', 0o755
@@ -57,7 +51,6 @@ module RailsActiveMcp
57
51
 
58
52
  # Rails Active MCP Wrapper Script
59
53
  # Ensures correct Ruby environment for Claude Desktop execution
60
- # Research-based solution for version manager compatibility
61
54
 
62
55
  # Fix Claude Desktop environment isolation issues
63
56
  export HOME="${HOME:-#{ENV['HOME']}}"
@@ -95,24 +88,6 @@ module RailsActiveMcp
95
88
  say 'Created environment wrapper at bin/rails-active-mcp-wrapper', :green
96
89
  end
97
90
 
98
- def create_mcp_route
99
- # Check if routes file exists and is writable
100
- routes_file = 'config/routes.rb'
101
- return unless File.exist?(routes_file)
102
-
103
- # Read current routes to check for conflicts
104
- routes_content = File.read(routes_file)
105
-
106
- if routes_content.include?('/mcp')
107
- say "Warning: Route '/mcp' already exists. Skipping route creation.", :yellow
108
- say "Manual setup: Add 'mount RailsActiveMcp::Engine, at: \"/mcp\"' to your routes.rb", :yellow
109
- else
110
- # Use Engine mounting instead of direct server mounting
111
- route "mount RailsActiveMcp::Engine, at: '/mcp'"
112
- say "Added MCP route at '/mcp'. You can change this in config/routes.rb", :green
113
- end
114
- end
115
-
116
91
  def create_mcp_config
117
92
  template 'mcp.ru', 'mcp.ru'
118
93
  end
@@ -134,7 +109,6 @@ module RailsActiveMcp
134
109
  say ' "mcpServers": {', :cyan
135
110
  say ' "rails-active-mcp": {', :cyan
136
111
  say " \"command\": \"#{Rails.root}/bin/rails-active-mcp-wrapper\",", :cyan
137
- say ' "args": ["stdio"],', :cyan
138
112
  say " \"cwd\": \"#{Rails.root}\",", :cyan
139
113
  say ' "env": {', :cyan
140
114
  say ' "RAILS_ENV": "development",', :cyan
@@ -144,6 +118,12 @@ module RailsActiveMcp
144
118
  say ' }', :cyan
145
119
  say '}', :cyan
146
120
  say '', :green
121
+ say "\nAvailable Tools in Claude Desktop:", :green
122
+ say '- console_execute: Execute Ruby code with safety checks', :yellow
123
+ say '- model_info: Get detailed information about Rails models', :yellow
124
+ say '- safe_query: Execute safe read-only database queries', :yellow
125
+ say '- dry_run: Analyze Ruby code for safety without execution', :yellow
126
+ say '', :green
147
127
  say "\nWhy use the wrapper?", :green
148
128
  say '- Handles Ruby version manager environments (asdf, rbenv, etc.)', :yellow
149
129
  say '- Prevents "bundler version" and "Ruby version" conflicts', :yellow
@@ -151,8 +131,8 @@ module RailsActiveMcp
151
131
  say "\nAlternative (if wrapper doesn't work):", :green
152
132
  say 'Use bin/rails-active-mcp-server instead of the wrapper', :yellow
153
133
  say "\nTesting:", :green
154
- say '1. Test manually: bin/rails-active-mcp-wrapper stdio', :yellow
155
- say '2. Should output JSON (not plain text)', :yellow
134
+ say '1. Test manually: bin/rails-active-mcp-wrapper', :yellow
135
+ say '2. Should output JSON responses (not plain text)', :yellow
156
136
  say '3. Restart Claude Desktop after config changes', :yellow
157
137
  say "\nTroubleshooting:", :green
158
138
  say '- Set RAILS_MCP_DEBUG=1 for verbose logging', :yellow
@@ -1,48 +1,32 @@
1
1
  ================================================================================
2
- Rails Active MCP has been installed!
2
+ Rails Active MCP Installation Complete!
3
3
  ================================================================================
4
4
 
5
- Configuration:
6
- - Initializer created at: config/initializers/rails_active_mcp.rb
7
- - Custom MCP server mounted at: /mcp
8
- - MCP server configuration: mcp.ru
9
- - Audit log will be created at: log/rails_active_mcp.log
5
+ 🎉 Rails Active MCP v2.0 with Official MCP Ruby SDK has been installed!
10
6
 
11
- Next Steps:
7
+ 📁 Files Created:
8
+ - config/initializers/rails_active_mcp.rb (Configuration)
9
+ - bin/rails-active-mcp-server (Main server executable)
10
+ - bin/rails-active-mcp-wrapper (Environment wrapper for Claude Desktop)
11
+ - mcp.ru (Rack configuration file)
12
12
 
13
- 1. Review and customize the configuration in config/initializers/rails_active_mcp.rb
13
+ 🛠️ Available MCP Tools:
14
+ - console_execute: Execute Ruby code with safety checks
15
+ - model_info: Get detailed information about Rails models
16
+ - safe_query: Execute safe read-only database queries
17
+ - dry_run: Analyze Ruby code for safety without execution
14
18
 
15
- 2. Configure server mode in config/initializers/rails_active_mcp.rb:
16
- config.server_mode = :stdio # For Claude Desktop (default)
17
- config.server_mode = :http # For HTTP-based integrations
18
- config.server_host = 'localhost'
19
- config.server_port = 3001
19
+ 🚀 Quick Start:
20
20
 
21
- 3. Start the MCP server:
22
- Option A: Use configured mode (RECOMMENDED)
23
- $ bundle exec rails-active-mcp-server
24
-
25
- Option B: Override mode via command line
26
- $ bundle exec rails-active-mcp-server stdio # Force stdio mode
27
- $ bundle exec rails-active-mcp-server http # Force HTTP mode
28
-
29
- Option C: Rails-mounted server (HTTP)
30
- $ rails server
31
-
32
- Option D: Using rackup
33
- $ rackup mcp.ru -p 3001
34
-
35
- 4. For Claude Desktop integration, add this to your Claude Desktop configuration:
36
-
37
- Location: ~/.config/claude-desktop/claude_desktop_config.json (Linux/macOS)
38
- Location: %APPDATA%\Claude\claude_desktop_config.json (Windows)
21
+ 1. Test the server:
22
+ $ ./bin/rails-active-mcp-server
39
23
 
24
+ 2. For Claude Desktop, add to claude_desktop_config.json:
40
25
  {
41
26
  "mcpServers": {
42
27
  "rails-active-mcp": {
43
- "command": "<%= Rails.root %>/bin/rails-active-mcp-wrapper",
44
- "args": ["stdio"],
45
- "cwd": "<%= Rails.root %>",
28
+ "command": "/path/to/your/project/bin/rails-active-mcp-wrapper",
29
+ "cwd": "/path/to/your/project",
46
30
  "env": {
47
31
  "RAILS_ENV": "development",
48
32
  "HOME": "/Users/your-username"
@@ -51,138 +35,20 @@ Next Steps:
51
35
  }
52
36
  }
53
37
 
54
- OR if you've installed the gem globally:
55
-
56
- {
57
- "mcpServers": {
58
- "rails-active-mcp": {
59
- "command": "rails-active-mcp-server",
60
- "args": ["stdio"],
61
- "cwd": "/path/to/your/rails/project"
62
- }
63
- }
64
- }
65
-
66
- 5. For other MCP clients (HTTP-based), add this to your MCP configuration:
67
- {
68
- "mcpServers": {
69
- "rails-console": {
70
- "command": "curl",
71
- "args": ["-X", "POST", "-H", "Content-Type: application/json", "-d", "@-", "http://localhost:3000/mcp"]
72
- }
73
- }
74
- }
75
-
76
- 6. Test the installation:
77
- $ rails console
78
- > RailsActiveMcp.safe?("User.count")
79
- > RailsActiveMcp.execute("User.count")
80
-
81
- Built-in Tools Available in Claude:
82
- - rails_console_execute: Execute Ruby code in Rails console with safety checks
83
- - rails_model_info: Get detailed information about Rails models
84
- - rails_safe_query: Execute safe read-only database queries
85
- - rails_dry_run: Analyze Ruby code for safety without execution
86
-
87
- Extend with custom tools by modifying the MCP server implementation.
88
-
89
- Security Notes:
90
- - Production mode enables strict safety by default
91
- - All executions are logged to the audit file
92
- - Dangerous operations are blocked in safe mode
93
- - Review the safety patterns in the configuration
94
-
95
- Transport Modes:
96
- - stdio: For Claude Desktop and compatible MCP clients (recommended)
97
- - http: For HTTP-based integrations and web applications
98
-
99
- Configuration Examples:
100
-
101
- For Claude Desktop (default):
102
- config.server_mode = :stdio
103
-
104
- For HTTP web integrations:
105
- config.server_mode = :http
106
- config.server_host = 'localhost'
107
- config.server_port = 3001
108
-
109
- For Docker/remote access:
110
- config.http_mode!(host: '0.0.0.0', port: 8080)
111
-
112
- For development with multiple transport modes:
113
- if Rails.env.development?
114
- config.stdio_mode! # Claude Desktop
115
- else
116
- config.http_mode!(host: '0.0.0.0', port: 3001) # Production HTTP
117
- end
118
-
119
- Custom MCP Server Benefits:
120
- - No external dependencies
121
- - Full control over implementation
122
- - Simplified deployment
123
- - Enhanced security
124
- - Works with Claude Desktop
125
-
126
- Debugging and Troubleshooting:
127
-
128
- For interactive debugging, use the MCP Inspector:
129
- $ bin/debug-mcp-server --mode inspector
130
-
131
- This will:
132
- - Launch the MCP Inspector connected to your server
133
- - Allow interactive testing of all tools
134
- - Show real-time debug output and logs
135
-
136
- Debug logging:
137
- $ RAILS_MCP_DEBUG=1 bundle exec rails-active-mcp-server stdio
138
-
139
- View Claude Desktop logs:
140
- $ tail -f ~/Library/Logs/Claude/mcp*.log # macOS
141
- $ tail -f ~/.config/claude-desktop/logs/*.log # Linux
142
-
143
- Common issues:
144
-
145
- 1. Ruby Version Manager Conflicts (Most Common Issue):
146
- If you see errors like "Could not find 'bundler' (X.X.X)" or "Your Ruby version is X.X.X, but your Gemfile specified Y.Y.Y":
147
-
148
- This happens because Claude Desktop uses system Ruby instead of your project's Ruby version.
149
-
150
- Solution A - Use the wrapper script (Recommended):
151
- In claude_desktop_config.json, use:
152
- "command": "<%= Rails.root %>/bin/rails-active-mcp-wrapper"
153
-
154
- Solution B - Create system symlink:
155
- $ sudo ln -sf $(which ruby) /usr/local/bin/ruby
156
-
157
- Solution C - Use absolute Ruby path:
158
- In claude_desktop_config.json, change "command" to your full Ruby path:
159
- "command": "$(which ruby)"
160
- "args": ["<%= Rails.root %>/bin/rails-active-mcp-server", "stdio"]
161
-
162
- 2. Environment Variable Issues:
163
- If you see "error loading config: $HOME is not defined":
164
-
165
- This happens because Claude Desktop doesn't inherit all environment variables.
166
-
167
- Solution: Add HOME to your env section in claude_desktop_config.json:
168
- "env": {
169
- "RAILS_ENV": "development",
170
- "HOME": "/Users/your-username"
171
- }
38
+ 3. Restart Claude Desktop and start using the tools!
172
39
 
173
- 3. General Environment Issues:
174
- - Ensure your Rails environment loads properly in the project directory
175
- - Check that the gem is properly installed and configured
176
- - Verify the Rails application starts without errors
177
- - Make sure the cwd path in Claude Desktop config is correct
40
+ ⚙️ Configuration:
41
+ Edit config/initializers/rails_active_mcp.rb to customize:
42
+ - allowed_commands: Shell commands that can be executed
43
+ - command_timeout: Execution timeout in seconds
44
+ - enable_logging: Enable/disable logging
45
+ - log_level: Logging verbosity (:debug, :info, :warn, :error)
178
46
 
179
- 4. Debug Steps:
180
- - Test manually: $ ./bin/rails-active-mcp-wrapper stdio
181
- - Should output JSON (not plain text)
182
- - Enable debug logging with RAILS_MCP_DEBUG=1 for detailed output
183
- - Check Claude logs: $ tail -f ~/Library/Logs/Claude/mcp*.log
184
- - Test environment variables: $ echo $HOME (should show your home directory)
47
+ 🔧 Troubleshooting:
48
+ - Use the wrapper script to avoid Ruby version manager conflicts
49
+ - Set RAILS_MCP_DEBUG=1 for verbose logging
50
+ - Ensure Claude Desktop has correct paths and environment variables
185
51
 
186
- For more information: https://github.com/goodpie/rails-active-mcp
52
+ 📚 Documentation: https://github.com/goodpie/rails-active-mcp
187
53
 
188
54
  ================================================================================
@@ -1,43 +1,42 @@
1
1
  require 'rails_active_mcp'
2
2
 
3
3
  RailsActiveMcp.configure do |config|
4
- # Enable/disable the MCP server
5
- config.enabled = true
6
-
7
- # Safety configuration
8
- config.safe_mode = Rails.env.production? # Always safe in production
9
- config.default_timeout = 30 # seconds
10
- config.max_results = 100
11
-
12
- # Model access control
13
- # config.allowed_models = %w[User Post Comment] # Empty means all allowed
14
- # config.blocked_models = %w[AdminUser Secret] # Models to never allow access
15
-
16
- # Mutation tools (dangerous operations)
17
- config.enable_mutation_tools = !Rails.env.production?
18
-
19
- # Logging and auditing
20
- config.log_executions = true
21
- config.audit_file = Rails.root.join('log', 'rails_active_mcp.log')
22
-
23
- # Server configuration
24
- config.server_mode = :stdio # :stdio for Claude Desktop, :http for web integrations
25
- config.server_host = 'localhost'
26
- config.server_port = 3001
27
-
28
- # Environment-specific settings
29
- case Rails.env
30
- when 'production'
31
- config.production_mode! # Very strict settings
32
- when 'development'
33
- config.permissive_mode! # More relaxed for development
34
- when 'test'
35
- config.strict_mode! # Safe for testing
4
+ # Core configuration options
5
+ config.allowed_commands = %w[
6
+ ls pwd cat head tail grep find wc
7
+ rails console rails runner
8
+ bundle exec rspec bundle exec test
9
+ git status git log git diff
10
+ ]
11
+
12
+ # Execution timeout in seconds
13
+ config.command_timeout = 30
14
+
15
+ # Logging configuration
16
+ config.enable_logging = true
17
+ config.log_level = :info # :debug, :info, :warn, :error
18
+
19
+ # Environment-specific adjustments
20
+ if Rails.env.production?
21
+ # More restrictive settings for production
22
+ config.log_level = :warn
23
+ config.command_timeout = 15
24
+ elsif Rails.env.development?
25
+ # More permissive settings for development
26
+ config.log_level = :debug
27
+ config.command_timeout = 60
36
28
  end
37
-
38
- # Add custom safety patterns
39
- # config.add_safety_pattern(/CustomDangerousMethod/, "Custom dangerous operation")
40
-
41
- # Operations that require manual confirmation
42
- config.require_confirmation_for = %i[delete destroy update_all delete_all]
43
29
  end
30
+
31
+ # Rails Active MCP is now ready!
32
+ #
33
+ # Available MCP Tools:
34
+ # - console_execute: Execute Ruby code with safety checks
35
+ # - model_info: Get detailed information about Rails models
36
+ # - safe_query: Execute safe read-only database queries
37
+ # - dry_run: Analyze Ruby code for safety without execution
38
+ #
39
+ # To start the server:
40
+ # bin/rails-active-mcp-server
41
+ #
42
+ # For Claude Desktop integration, see the post-install instructions.
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'config/environment'
4
- require 'rails_active_mcp'
4
+ require 'rails_active_mcp/sdk/server'
5
5
 
6
- # Run the Rails Active MCP server
7
- run RailsActiveMcp::McpServer.new
6
+ # Run the Rails Active MCP server using the official MCP Ruby SDK
7
+ # Note: This file is primarily for reference. The recommended way to run
8
+ # the server is using: bin/rails-active-mcp-server
9
+
10
+ server = RailsActiveMcp::SDK::Server.new
11
+ server.run
@@ -1,119 +1,58 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  module RailsActiveMcp
4
6
  class Configuration
5
- attr_accessor :enabled, :safe_mode, :default_timeout, :max_results,
6
- :allowed_models, :blocked_models, :custom_safety_patterns,
7
- :log_executions, :audit_file, :enable_mutation_tools,
8
- :require_confirmation_for, :execution_environment, :server_mode,
9
- :server_host, :server_port
7
+ # Core configuration options
8
+ attr_accessor :allowed_commands, :command_timeout, :enable_logging, :log_level
9
+
10
+ # Safety and execution options
11
+ attr_accessor :safe_mode, :default_timeout, :max_results, :log_executions, :audit_file
12
+ attr_accessor :custom_safety_patterns, :allowed_models
10
13
 
11
14
  def initialize
12
- @enabled = true
15
+ @allowed_commands = %w[
16
+ ls pwd cat head tail grep find wc
17
+ rails console rails runner
18
+ bundle exec rspec bundle exec test
19
+ git status git log git diff
20
+ ]
21
+ @command_timeout = 30
22
+ @enable_logging = true
23
+ @log_level = :info
24
+
25
+ # Safety and execution defaults
13
26
  @safe_mode = true
14
27
  @default_timeout = 30
15
28
  @max_results = 100
16
- @allowed_models = [] # Empty means all models allowed
17
- @blocked_models = []
29
+ @log_executions = false
30
+ @audit_file = nil
18
31
  @custom_safety_patterns = []
19
- @log_executions = true
20
- # Safe Rails.root access
21
- if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
22
- @audit_file = rails_root_join('log',
23
- 'rails_active_mcp.log')
24
- end
25
- @enable_mutation_tools = false
26
- @require_confirmation_for = %i[delete destroy update_all delete_all]
27
- @execution_environment = :current # :current, :sandbox, :readonly_replica
28
- @server_mode = :stdio # :stdio, :http
29
- @server_host = 'localhost'
30
- @server_port = 3001
31
- end
32
-
33
- # Safety configuration
34
- def strict_mode!
35
- @safe_mode = true
36
- @enable_mutation_tools = false
37
- @default_timeout = 15
38
- @max_results = 50
39
- end
40
-
41
- def permissive_mode!
42
- @safe_mode = false
43
- @enable_mutation_tools = true
44
- @default_timeout = 60
45
- @max_results = 1000
46
- end
47
-
48
- def production_mode!
49
- strict_mode!
50
- @execution_environment = :readonly_replica
51
- @log_executions = true
52
- @require_confirmation_for = %i[delete destroy update create save]
53
- end
54
-
55
- # Model access configuration
56
- def allow_models(*models)
57
- @allowed_models.concat(models.map(&:to_s))
58
- end
59
-
60
- def block_models(*models)
61
- @blocked_models.concat(models.map(&:to_s))
32
+ @allowed_models = []
62
33
  end
63
34
 
64
- def add_safety_pattern(pattern, description = nil)
65
- @custom_safety_patterns << { pattern: pattern, description: description }
66
- end
67
-
68
- # Server configuration
69
- def stdio_mode!
70
- @server_mode = :stdio
71
- end
72
-
73
- def http_mode!(host: 'localhost', port: 3001)
74
- @server_mode = :http
75
- @server_host = host
76
- @server_port = port
77
- end
78
-
79
- def server_mode_valid?
80
- %i[stdio http].include?(@server_mode)
81
- end
82
-
83
- # Validation
84
35
  def model_allowed?(model_name)
85
- model_str = model_name.to_s
86
-
87
- # Check if specifically blocked
88
- return false if @blocked_models.include?(model_str)
89
-
90
- # If allow list is empty, allow all (except blocked)
91
- return true if @allowed_models.empty?
36
+ return true if @allowed_models.empty? # Allow all if none specified
92
37
 
93
- # Check allow list
94
- @allowed_models.include?(model_str)
38
+ @allowed_models.include?(model_name.to_s)
95
39
  end
96
40
 
97
- def validate!
98
- raise ArgumentError, 'timeout must be positive' if @default_timeout <= 0
99
- raise ArgumentError, 'max_results must be positive' if @max_results <= 0
100
- raise ArgumentError, "invalid server_mode: #{@server_mode}" unless server_mode_valid?
101
- raise ArgumentError, 'server_port must be positive' if @server_port <= 0
102
-
103
- return unless defined?(Rails) && @audit_file
104
-
105
- audit_dir = File.dirname(@audit_file)
106
- FileUtils.mkdir_p(audit_dir) unless File.directory?(audit_dir)
41
+ def valid?
42
+ allowed_commands.is_a?(Array) &&
43
+ command_timeout.is_a?(Numeric) && command_timeout > 0 &&
44
+ [true, false].include?(enable_logging) &&
45
+ %i[debug info warn error].include?(log_level) &&
46
+ [true, false].include?(safe_mode) &&
47
+ default_timeout.is_a?(Numeric) && default_timeout > 0 &&
48
+ max_results.is_a?(Numeric) && max_results > 0 &&
49
+ [true, false].include?(log_executions) &&
50
+ custom_safety_patterns.is_a?(Array) &&
51
+ allowed_models.is_a?(Array)
107
52
  end
108
53
 
109
- private
110
-
111
- def rails_root_join(*args)
112
- if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
113
- Rails.root.join(*args)
114
- else
115
- File.join(Dir.pwd, *args)
116
- end
54
+ def reset!
55
+ initialize
117
56
  end
118
57
  end
119
58
  end
@@ -194,10 +194,13 @@ module RailsActiveMcp
194
194
  def execute_with_captured_output(code)
195
195
  # Thread-safe output capture using mutex
196
196
  @execution_mutex.synchronize do
197
- # Capture both stdout and the return value
197
+ # Capture both stdout and stderr to prevent any Rails output leakage
198
198
  old_stdout = $stdout
199
+ old_stderr = $stderr
199
200
  captured_output = StringIO.new
201
+ captured_errors = StringIO.new
200
202
  $stdout = captured_output
203
+ $stderr = captured_errors
201
204
 
202
205
  begin
203
206
  # Create thread-safe execution context
@@ -209,17 +212,22 @@ module RailsActiveMcp
209
212
  execution_time = Time.now - start_time
210
213
 
211
214
  output = captured_output.string
215
+ errors = captured_errors.string
216
+
217
+ # Combine output and errors for comprehensive result
218
+ combined_output = [output, errors].reject(&:empty?).join("\n")
212
219
 
213
220
  {
214
221
  success: true,
215
222
  return_value: return_value,
216
- output: output,
223
+ output: combined_output,
217
224
  return_value_string: safe_inspect(return_value),
218
225
  execution_time: execution_time,
219
226
  code: code
220
227
  }
221
228
  rescue StandardError => e
222
229
  execution_time = Time.now - start_time if defined?(start_time)
230
+ errors = captured_errors.string
223
231
 
224
232
  {
225
233
  success: false,
@@ -227,10 +235,12 @@ module RailsActiveMcp
227
235
  error_class: e.class.name,
228
236
  backtrace: e.backtrace&.first(10),
229
237
  execution_time: execution_time,
230
- code: code
238
+ code: code,
239
+ stderr: errors.empty? ? nil : errors
231
240
  }
232
241
  ensure
233
242
  $stdout = old_stdout if old_stdout
243
+ $stderr = old_stderr if old_stderr
234
244
  end
235
245
  end
236
246
  end