rails-active-mcp 0.1.7 → 2.0.8

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 (33) 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/examples/rails_app_integration.md +405 -0
  7. data/exe/rails-active-mcp-server +153 -76
  8. data/gemfiles/rails_7.1.gemfile +34 -0
  9. data/lib/generators/rails_active_mcp/install/install_generator.rb +19 -39
  10. data/lib/generators/rails_active_mcp/install/templates/README.md +134 -188
  11. data/lib/generators/rails_active_mcp/install/templates/initializer.rb +65 -28
  12. data/lib/generators/rails_active_mcp/install/templates/mcp.ru +7 -3
  13. data/lib/rails_active_mcp/configuration.rb +37 -98
  14. data/lib/rails_active_mcp/console_executor.rb +13 -3
  15. data/lib/rails_active_mcp/engine.rb +36 -24
  16. data/lib/rails_active_mcp/sdk/server.rb +183 -0
  17. data/lib/rails_active_mcp/sdk/tools/console_execute_tool.rb +103 -0
  18. data/lib/rails_active_mcp/sdk/tools/dry_run_tool.rb +73 -0
  19. data/lib/rails_active_mcp/sdk/tools/model_info_tool.rb +106 -0
  20. data/lib/rails_active_mcp/sdk/tools/safe_query_tool.rb +77 -0
  21. data/lib/rails_active_mcp/tasks.rake +236 -80
  22. data/lib/rails_active_mcp/version.rb +1 -1
  23. data/lib/rails_active_mcp.rb +5 -11
  24. data/rails_active_mcp.gemspec +62 -11
  25. metadata +83 -24
  26. data/app/controllers/rails_active_mcp/mcp_controller.rb +0 -80
  27. data/lib/rails_active_mcp/mcp_server.rb +0 -383
  28. data/lib/rails_active_mcp/railtie.rb +0 -70
  29. data/lib/rails_active_mcp/stdio_server.rb +0 -517
  30. data/lib/rails_active_mcp/tools/console_execute_tool.rb +0 -61
  31. data/lib/rails_active_mcp/tools/dry_run_tool.rb +0 -41
  32. data/lib/rails_active_mcp/tools/model_info_tool.rb +0 -70
  33. data/lib/rails_active_mcp/tools/safe_query_tool.rb +0 -41
@@ -0,0 +1,405 @@
1
+ # Rails Active MCP - Real-World Integration Examples
2
+
3
+ This guide demonstrates how to integrate Rails Active MCP in various real-world scenarios, following the best practices outlined in the [Rails application structure guide](https://dev.to/kimrgrey/tuning-rails-application-structure-5f74).
4
+
5
+ ## E-commerce Application Example
6
+
7
+ ### Model Setup
8
+ ```ruby
9
+ # app/models/user.rb
10
+ class User < ApplicationRecord
11
+ has_many :orders, dependent: :destroy
12
+ has_one :profile, dependent: :destroy
13
+
14
+ validates :email, presence: true, uniqueness: true
15
+ scope :active, -> { where(active: true) }
16
+ end
17
+
18
+ # app/models/order.rb
19
+ class Order < ApplicationRecord
20
+ belongs_to :user
21
+ has_many :order_items, dependent: :destroy
22
+ has_many :products, through: :order_items
23
+
24
+ validates :total_amount, presence: true, numericality: { greater_than: 0 }
25
+ scope :recent, -> { where('created_at > ?', 1.week.ago) }
26
+ end
27
+
28
+ # app/models/product.rb
29
+ class Product < ApplicationRecord
30
+ has_many :order_items
31
+ has_many :orders, through: :order_items
32
+
33
+ validates :name, presence: true
34
+ validates :price, presence: true, numericality: { greater_than: 0 }
35
+ scope :available, -> { where(available: true) }
36
+ end
37
+ ```
38
+
39
+ ### Configuration for E-commerce
40
+ ```ruby
41
+ # config/initializers/rails_active_mcp.rb
42
+ RailsActiveMcp.configure do |config|
43
+ # Core safety settings
44
+ config.safe_mode = true
45
+ config.max_results = 100
46
+ config.command_timeout = 30
47
+
48
+ # Environment-specific settings
49
+ case Rails.env
50
+ when 'production'
51
+ # Strict production settings
52
+ config.safe_mode = true
53
+ config.max_results = 50
54
+ config.command_timeout = 15
55
+ config.log_executions = true
56
+
57
+ # Only allow safe models in production
58
+ config.allowed_models = %w[User Order Product OrderItem]
59
+
60
+ when 'development'
61
+ # More permissive for development
62
+ config.safe_mode = false
63
+ config.max_results = 200
64
+ config.command_timeout = 60
65
+ config.log_level = :debug
66
+
67
+ when 'staging'
68
+ # Production-like but slightly more permissive
69
+ config.safe_mode = true
70
+ config.max_results = 100
71
+ config.command_timeout = 30
72
+ config.log_executions = true
73
+ end
74
+
75
+ # Custom safety patterns for e-commerce
76
+ config.custom_safety_patterns = [
77
+ {
78
+ pattern: /payment.*delete|destroy.*payment/i,
79
+ description: "Payment data modification is dangerous"
80
+ },
81
+ {
82
+ pattern: /User.*\.update_all.*role/i,
83
+ description: "Mass role updates are dangerous"
84
+ }
85
+ ]
86
+ end
87
+ ```
88
+
89
+ ### Claude Desktop Queries for E-commerce
90
+
91
+ #### Sales Analytics
92
+ ```ruby
93
+ # Ask Claude: "What are our sales metrics for the last week?"
94
+ Order.recent.sum(:total_amount)
95
+ Order.recent.count
96
+ Order.recent.average(:total_amount)
97
+
98
+ # Ask Claude: "Who are our top customers by order value?"
99
+ User.joins(:orders)
100
+ .group('users.id', 'users.email')
101
+ .order('SUM(orders.total_amount) DESC')
102
+ .limit(10)
103
+ .pluck('users.email', 'SUM(orders.total_amount)')
104
+ ```
105
+
106
+ #### Inventory Management
107
+ ```ruby
108
+ # Ask Claude: "Which products are running low on inventory?"
109
+ Product.where('inventory_count < ?', 10)
110
+ .order(:inventory_count)
111
+ .pluck(:name, :inventory_count)
112
+
113
+ # Ask Claude: "What are our best-selling products this month?"
114
+ Product.joins(:order_items)
115
+ .where(order_items: { created_at: 1.month.ago.. })
116
+ .group(:name)
117
+ .order('COUNT(*) DESC')
118
+ .limit(10)
119
+ .count
120
+ ```
121
+
122
+ #### Customer Support
123
+ ```ruby
124
+ # Ask Claude: "Find recent orders for customer email@example.com"
125
+ User.find_by(email: 'email@example.com')
126
+ &.orders
127
+ &.recent
128
+ &.includes(:products)
129
+ &.map { |o| { id: o.id, total: o.total_amount, products: o.products.pluck(:name) } }
130
+
131
+ # Ask Claude: "Check the User model structure"
132
+ # Uses model_info tool to show schema, associations, validations
133
+ ```
134
+
135
+ ## SaaS Application Example
136
+
137
+ ### Multi-tenant Setup
138
+ ```ruby
139
+ # app/models/account.rb
140
+ class Account < ApplicationRecord
141
+ has_many :users, dependent: :destroy
142
+ has_many :projects, dependent: :destroy
143
+
144
+ validates :name, presence: true
145
+ validates :plan, inclusion: { in: %w[free pro enterprise] }
146
+ end
147
+
148
+ # app/models/project.rb
149
+ class Project < ApplicationRecord
150
+ belongs_to :account
151
+ belongs_to :user, -> { where(role: 'owner') }
152
+ has_many :tasks, dependent: :destroy
153
+
154
+ validates :name, presence: true
155
+ scope :active, -> { where(archived: false) }
156
+ end
157
+ ```
158
+
159
+ ### SaaS-specific Configuration
160
+ ```ruby
161
+ # config/initializers/rails_active_mcp.rb
162
+ RailsActiveMcp.configure do |config|
163
+ config.safe_mode = true
164
+ config.max_results = 100
165
+
166
+ # Tenant isolation safety
167
+ config.custom_safety_patterns = [
168
+ {
169
+ pattern: /Account.*delete_all|destroy_all/i,
170
+ description: "Account mass operations are forbidden"
171
+ },
172
+ {
173
+ pattern: /User.*update_all.*account_id/i,
174
+ description: "Cross-tenant user moves are dangerous"
175
+ }
176
+ ]
177
+
178
+ # Production tenant restrictions
179
+ if Rails.env.production?
180
+ config.allowed_models = %w[Account User Project Task]
181
+ config.max_results = 50
182
+ end
183
+ end
184
+ ```
185
+
186
+ ### Claude Queries for SaaS Analytics
187
+ ```ruby
188
+ # Ask Claude: "How many active accounts do we have by plan?"
189
+ Account.group(:plan).count
190
+
191
+ # Ask Claude: "What's our monthly recurring revenue?"
192
+ Account.where(plan: ['pro', 'enterprise'])
193
+ .sum('CASE
194
+ WHEN plan = "pro" THEN 29
195
+ WHEN plan = "enterprise" THEN 99
196
+ ELSE 0 END')
197
+
198
+ # Ask Claude: "Which accounts have the most projects?"
199
+ Account.joins(:projects)
200
+ .group('accounts.name')
201
+ .order('COUNT(projects.id) DESC')
202
+ .limit(10)
203
+ .count
204
+ ```
205
+
206
+ ## Content Management System Example
207
+
208
+ ### CMS Model Structure
209
+ ```ruby
210
+ # app/models/article.rb
211
+ class Article < ApplicationRecord
212
+ belongs_to :author, class_name: 'User'
213
+ belongs_to :category
214
+ has_many :comments, dependent: :destroy
215
+
216
+ validates :title, presence: true
217
+ validates :content, presence: true
218
+
219
+ scope :published, -> { where(published: true) }
220
+ scope :recent, -> { order(created_at: :desc) }
221
+ end
222
+
223
+ # app/models/category.rb
224
+ class Category < ApplicationRecord
225
+ has_many :articles
226
+ validates :name, presence: true, uniqueness: true
227
+ end
228
+ ```
229
+
230
+ ### CMS Configuration
231
+ ```ruby
232
+ # config/initializers/rails_active_mcp.rb
233
+ RailsActiveMcp.configure do |config|
234
+ config.safe_mode = true
235
+
236
+ # Content-specific safety patterns
237
+ config.custom_safety_patterns = [
238
+ {
239
+ pattern: /Article.*delete_all.*published.*true/i,
240
+ description: "Mass deletion of published articles is dangerous"
241
+ }
242
+ ]
243
+
244
+ # Environment-specific settings
245
+ case Rails.env
246
+ when 'production'
247
+ config.allowed_models = %w[Article Category User Comment]
248
+ config.max_results = 25 # Smaller for content queries
249
+ end
250
+ end
251
+ ```
252
+
253
+ ### Claude Queries for Content Analytics
254
+ ```ruby
255
+ # Ask Claude: "What are our most popular categories?"
256
+ Category.joins(:articles)
257
+ .where(articles: { published: true })
258
+ .group(:name)
259
+ .order('COUNT(articles.id) DESC')
260
+ .count
261
+
262
+ # Ask Claude: "Show me recent article performance"
263
+ Article.published
264
+ .recent
265
+ .limit(10)
266
+ .pluck(:title, :views_count, :created_at)
267
+
268
+ # Ask Claude: "Find articles that need moderation"
269
+ Article.joins(:comments)
270
+ .where(comments: { flagged: true })
271
+ .distinct
272
+ .pluck(:title, :id)
273
+ ```
274
+
275
+ ## Advanced Safety Patterns
276
+
277
+ ### Custom Validators
278
+ ```ruby
279
+ # config/initializers/rails_active_mcp.rb
280
+ RailsActiveMcp.configure do |config|
281
+ # Financial data protection
282
+ config.custom_safety_patterns += [
283
+ { pattern: /payment|billing|card|bank/i, description: "Financial data access" },
284
+ { pattern: /password|token|secret|key/i, description: "Sensitive credential access" },
285
+ { pattern: /delete.*where.*id.*in/i, description: "Bulk deletion by ID list" }
286
+ ]
287
+
288
+ # Model-specific restrictions
289
+ config.allowed_models = case Rails.env
290
+ when 'production'
291
+ %w[User Order Product Customer Invoice] # Whitelist approach
292
+ else
293
+ [] # Empty = allow all models
294
+ end
295
+ end
296
+ ```
297
+
298
+ ### Environment-specific Presets
299
+ ```ruby
300
+ # config/initializers/rails_active_mcp.rb
301
+ RailsActiveMcp.configure do |config|
302
+ case Rails.env
303
+ when 'production'
304
+ # Ultra-safe production mode
305
+ config.safe_mode = true
306
+ config.command_timeout = 10
307
+ config.max_results = 25
308
+ config.log_executions = true
309
+
310
+ when 'staging'
311
+ # Production-like testing
312
+ config.safe_mode = true
313
+ config.command_timeout = 20
314
+ config.max_results = 50
315
+ config.log_executions = true
316
+
317
+ when 'development'
318
+ # Developer-friendly
319
+ config.safe_mode = false
320
+ config.command_timeout = 60
321
+ config.max_results = 200
322
+ config.log_level = :debug
323
+
324
+ when 'test'
325
+ # Fast and minimal for tests
326
+ config.safe_mode = true
327
+ config.command_timeout = 5
328
+ config.max_results = 10
329
+ config.log_executions = false
330
+ end
331
+ end
332
+ ```
333
+
334
+ ## Best Practices
335
+
336
+ ### 1. Always Use Limits
337
+ ```ruby
338
+ # ✅ Good - Always include limits
339
+ User.where(active: true).limit(10)
340
+ Order.recent.limit(20)
341
+
342
+ # ❌ Avoid - Unlimited queries can overwhelm Claude
343
+ User.all
344
+ Order.where(status: 'pending')
345
+ ```
346
+
347
+ ### 2. Prefer Aggregations Over Raw Data
348
+ ```ruby
349
+ # ✅ Good - Summary data
350
+ User.group(:status).count
351
+ Order.group_by_day(:created_at).sum(:total_amount)
352
+
353
+ # ❌ Less useful - Raw data dumps
354
+ User.pluck(:email, :status, :created_at)
355
+ ```
356
+
357
+ ### 3. Use Meaningful Scopes
358
+ ```ruby
359
+ # ✅ Good - Readable business logic
360
+ User.active.recent_signups.count
361
+ Order.completed.this_month.sum(:total_amount)
362
+
363
+ # ❌ Less clear - Complex inline conditions
364
+ User.where(active: true, created_at: 1.week.ago..).count
365
+ ```
366
+
367
+ ### 4. Structure Claude Queries Naturally
368
+ ```ruby
369
+ # Ask Claude natural questions:
370
+ # "How many users signed up this week?"
371
+ # "What's our average order value?"
372
+ # "Which products need restocking?"
373
+ # "Show me the User model structure"
374
+ ```
375
+
376
+ ## Troubleshooting Common Issues
377
+
378
+ ### Performance Issues
379
+ ```ruby
380
+ # Monitor execution times
381
+ rails rails_active_mcp:benchmark
382
+
383
+ # Check for slow queries
384
+ rails rails_active_mcp:status
385
+ ```
386
+
387
+ ### Safety Violations
388
+ ```ruby
389
+ # Test code safety before asking Claude
390
+ rails rails_active_mcp:check_safety['User.delete_all']
391
+
392
+ # View current configuration
393
+ rails rails_active_mcp:validate_config
394
+ ```
395
+
396
+ ### Claude Desktop Integration
397
+ ```ruby
398
+ # Generate Claude Desktop config
399
+ rails rails_active_mcp:install_claude_config
400
+
401
+ # Test server connectivity
402
+ bin/rails-active-mcp-wrapper
403
+ ```
404
+
405
+ This comprehensive integration guide helps Rails developers understand how to effectively use Rails Active MCP in real-world applications, following modern Rails patterns and ensuring secure, efficient AI-powered database interactions.
@@ -1,120 +1,197 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'rack'
4
+ # Require essential gems first
5
5
  require 'json'
6
6
  require 'stringio'
7
7
 
8
- # Suppress ALL output immediately when not in debug mode to prevent MCP JSON protocol interference
8
+ # CRITICAL: Redirect stdout IMMEDIATELY for stdio mode to prevent any output interference
9
+ # This must happen before any other gem loading
9
10
  original_stdout = $stdout
10
11
  original_stderr = $stderr
11
12
 
12
- # Only suppress if we're likely running in stdio mode (for Claude) and not debugging
13
- if ARGV.first == 'stdio' && !ENV['RAILS_MCP_DEBUG']
14
- # Redirect to temporary StringIO until Rails loading is complete
15
- $stdout = StringIO.new
16
- $stderr = StringIO.new
13
+ # Determine if we should redirect output (stdio mode or explicit request)
14
+ should_redirect = (ARGV.first == 'stdio' || ARGV.first.nil?) && !ENV['RAILS_MCP_DEBUG']
15
+
16
+ if should_redirect
17
+ # Create log directory early
18
+ log_dir = File.join(Dir.pwd, 'log')
19
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
20
+
21
+ # Redirect stderr to log file immediately, before any loading
22
+ stderr_log = File.join(log_dir, 'rails_mcp_stderr.log')
23
+ $stderr.reopen(stderr_log, 'a')
24
+ $stderr.sync = true
25
+
26
+ # Use StringIO to capture any unwanted stdout during loading
27
+ # We'll filter out non-JSON content later
28
+ captured_stdout = StringIO.new
29
+ $stdout = captured_stdout
17
30
  end
18
31
 
19
32
  # Initialize Rails environment if available (from current working directory)
20
33
  rails_loaded = false
34
+ rails_load_error = nil
21
35
  if File.exist?('config/environment.rb')
22
36
  begin
23
37
  require './config/environment'
24
38
  rails_loaded = true
39
+
40
+ # Log successful Rails loading
41
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Rails environment loaded successfully" if should_redirect
25
42
  rescue StandardError => e
26
43
  # Rails loading failed, continue without it
27
- # Only show warning in debug mode
28
- warn "Warning: Failed to load Rails environment: #{e.message}" if ENV['RAILS_MCP_DEBUG']
44
+ rails_load_error = e
45
+ # Log to stderr (which is already redirected if needed)
46
+ warn "[#{Time.now}] [RAILS-MCP] WARNING: Failed to load Rails environment: #{e.message}"
47
+ warn "[#{Time.now}] [RAILS-MCP] WARNING: #{e.backtrace.first(3).join("\n")}" if ENV['RAILS_MCP_DEBUG']
29
48
  end
49
+ elsif should_redirect
50
+ warn "[#{Time.now}] [RAILS-MCP] INFO: No Rails environment found (config/environment.rb missing)"
30
51
  end
31
52
 
32
- # Restore stdout/stderr after Rails loading for non-debug stdio mode
33
- if ARGV.first == 'stdio' && !ENV['RAILS_MCP_DEBUG']
53
+ # Restore stdout after Rails loading for stdio mode (stderr stays redirected)
54
+ if should_redirect
55
+ # Check if anything was captured during loading
56
+ captured_content = captured_stdout.string
57
+ unless captured_content.empty?
58
+ # Log any captured content to stderr for debugging
59
+ warn "[#{Time.now}] [RAILS-MCP] WARNING: Captured stdout during loading: #{captured_content.inspect}"
60
+ $stderr.flush
61
+ end
62
+
63
+ # Restore original stdout
34
64
  $stdout = original_stdout
35
- $stderr = original_stderr
36
65
  end
37
66
 
38
67
  # Now load the gem
39
- require_relative '../lib/rails_active_mcp'
40
-
41
- # Parse command line options with config defaults (only after Rails is loaded)
42
- default_mode = if rails_loaded && defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config)
43
- RailsActiveMcp.config.server_mode.to_s
44
- else
45
- 'stdio'
46
- end
47
- default_port = if rails_loaded && defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config)
48
- RailsActiveMcp.config.server_port
49
- else
50
- 3001
51
- end
52
- default_host = if rails_loaded && defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config)
53
- RailsActiveMcp.config.server_host
54
- else
55
- 'localhost'
56
- end
57
-
58
- transport = ARGV[0] || default_mode
59
- port = ARGV.include?('--port') ? ARGV[ARGV.index('--port') + 1].to_i : default_port
60
- host = ARGV.include?('--host') ? ARGV[ARGV.index('--host') + 1] : default_host
68
+ begin
69
+ require_relative '../lib/rails_active_mcp'
70
+ rescue LoadError => e
71
+ warn "[#{Time.now}] [RAILS-MCP] FATAL: Failed to load rails_active_mcp gem: #{e.message}"
72
+ exit(1)
73
+ end
74
+
75
+ # Parse command line options
76
+ transport = ARGV[0] || 'stdio'
77
+ port = ARGV.include?('--port') ? ARGV[ARGV.index('--port') + 1].to_i : 3001
78
+ host = ARGV.include?('--host') ? ARGV[ARGV.index('--host') + 1] : 'localhost'
79
+
80
+ # Determine and set correct working directory
81
+ def find_rails_root(start_dir = Dir.pwd)
82
+ current_dir = start_dir
83
+
84
+ # Look for Gemfile and config/environment.rb up to 5 levels up
85
+ 5.times do
86
+ gemfile_path = File.join(current_dir, 'Gemfile')
87
+ config_path = File.join(current_dir, 'config', 'environment.rb')
88
+
89
+ return current_dir if File.exist?(gemfile_path) && File.exist?(config_path)
90
+
91
+ parent_dir = File.dirname(current_dir)
92
+ break if parent_dir == current_dir # reached root
93
+
94
+ current_dir = parent_dir
95
+ end
96
+
97
+ nil
98
+ end
99
+
100
+ # Auto-detect Rails root and change directory if needed
101
+ rails_root = find_rails_root
102
+ if rails_root && rails_root != Dir.pwd
103
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Detected Rails root at #{rails_root}, changing directory" if should_redirect
104
+ Dir.chdir(rails_root)
105
+ elsif rails_root.nil?
106
+ # Check if we're in the gem directory and need to find a Rails app
107
+ if File.basename(Dir.pwd) == 'rails-active-mcp-gem' || Dir.pwd.include?('rails-active-mcp-gem')
108
+ warn "[#{Time.now}] [RAILS-MCP] ERROR: Running from gem directory, not Rails application directory"
109
+ warn "[#{Time.now}] [RAILS-MCP] ERROR: Please run the server from your Rails application root directory"
110
+ warn "[#{Time.now}] [RAILS-MCP] ERROR: Example: cd /path/to/your/rails/app && /path/to/rails-active-mcp-gem/exe/rails-active-mcp-server stdio"
111
+ exit(1)
112
+ end
113
+
114
+ # Final check for Gemfile in current directory
115
+ unless File.exist?('Gemfile')
116
+ warn "[#{Time.now}] [RAILS-MCP] ERROR: Could not locate Gemfile in current directory: #{Dir.pwd}"
117
+ warn "[#{Time.now}] [RAILS-MCP] ERROR: Please ensure you're running from a Rails application root directory"
118
+ warn "[#{Time.now}] [RAILS-MCP] ERROR: Current directory contents:"
119
+ Dir.entries('.').each { |entry| warn "[#{Time.now}] [RAILS-MCP] ERROR: #{entry}" unless entry.start_with?('.') }
120
+ exit(1)
121
+ end
122
+ end
123
+
124
+ # Log the working directory for debugging
125
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Working directory: #{Dir.pwd}" if should_redirect
126
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Gemfile found: #{File.exist?('Gemfile')}" if should_redirect
61
127
 
62
128
  case transport
63
129
  when 'stdio'
64
- # Stdio transport for Claude Desktop
65
- require_relative '../lib/rails_active_mcp/stdio_server'
66
-
130
+ # Stdio transport for Claude Desktop using MCP SDK
67
131
  begin
68
- # Final stderr redirection to prevent any remaining output from interfering with MCP JSON protocol
69
- # This is critical because Claude Desktop expects ONLY JSON on stdout and ANY non-JSON breaks the protocol
70
- unless ENV['RAILS_MCP_DEBUG']
71
- # Create log directory if it doesn't exist
72
- log_dir = File.join(Dir.pwd, 'log')
73
- Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
74
-
75
- # Redirect stderr to log file
76
- stderr_log = File.join(log_dir, 'rails_mcp_stderr.log')
77
- $stderr.reopen(stderr_log, 'a')
78
- $stderr.sync = true
132
+ require_relative '../lib/rails_active_mcp/sdk/server'
133
+
134
+ # Log startup information
135
+ if should_redirect
136
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Starting Rails Active MCP Server v#{RailsActiveMcp::VERSION}"
137
+ end
138
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Rails loaded: #{rails_loaded}" if should_redirect
139
+ if rails_load_error && should_redirect
140
+ warn "[#{Time.now}] [RAILS-MCP] WARNING: Rails load error: #{rails_load_error.class.name}: #{rails_load_error.message}"
79
141
  end
80
142
 
81
- stdio_server = RailsActiveMcp::StdioServer.new
82
- stdio_server.start
143
+ server = RailsActiveMcp::Sdk::Server.new
144
+ server.run_stdio
83
145
  rescue Interrupt
146
+ warn "[#{Time.now}] [RAILS-MCP] INFO: Server interrupted by user" if should_redirect
84
147
  exit(0)
148
+ rescue LoadError => e
149
+ warn "[#{Time.now}] [RAILS-MCP] FATAL: MCP SDK not available: #{e.message}"
150
+ warn "[#{Time.now}] [RAILS-MCP] FATAL: Please install the MCP gem: gem install mcp"
151
+ exit(1)
152
+ rescue StandardError => e
153
+ warn "[#{Time.now}] [RAILS-MCP] FATAL: Server startup failed: #{e.message}"
154
+ warn "[#{Time.now}] [RAILS-MCP] FATAL: #{e.backtrace.join("\n")}" if ENV['RAILS_MCP_DEBUG']
155
+ exit(1)
85
156
  end
86
157
 
87
158
  when 'http'
88
- # HTTP transport for other integrations
89
- puts 'Warning: Rails environment not loaded. Some features may not work properly.' unless rails_loaded
90
-
91
- puts "Starting Rails Active MCP Server on #{host}:#{port}"
92
- puts 'Press Ctrl+C to stop'
159
+ puts 'Error: HTTP transport is no longer supported in v2.0.0'
160
+ puts 'Please use stdio mode for MCP integration: rails-active-mcp-server stdio'
161
+ puts ''
162
+ puts 'For more information, see the migration guide in the documentation.'
163
+ exit(1)
93
164
 
94
- begin
95
- require 'webrick'
96
- require 'rack/handler/webrick'
97
-
98
- Rack::Handler::WEBrick.run(
99
- RailsActiveMcp::McpServer.new,
100
- Port: port,
101
- Host: host,
102
- Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN)
103
- )
104
- rescue LoadError => e
105
- puts 'Error: WEBrick not available. Please install: gem install webrick'
106
- puts 'Or use stdio mode for Claude Desktop: rails-active-mcp-server stdio'
107
- exit(1)
108
- rescue Interrupt
109
- puts "\nShutting down server..."
110
- end
165
+ when '--help', '-h'
166
+ puts 'Rails Active MCP Server'
167
+ puts "Version: #{RailsActiveMcp::VERSION}"
168
+ puts ''
169
+ puts 'Usage: rails-active-mcp-server [stdio] [options]'
170
+ puts ' stdio: For MCP integration (default)'
171
+ puts ''
172
+ puts 'Options:'
173
+ puts ' --help, -h Show this help message'
174
+ puts ''
175
+ puts 'Environment variables:'
176
+ puts ' RAILS_MCP_DEBUG=1 Enable debug logging and disable output redirection'
177
+ puts ''
178
+ puts 'Examples:'
179
+ puts ' rails-active-mcp-server stdio # Start in stdio mode'
180
+ puts ' RAILS_MCP_DEBUG=1 rails-active-mcp-server # Start with debug logging'
181
+ puts ''
182
+ puts 'Note: HTTP transport was removed in v2.0.0. Use stdio mode for MCP integration.'
183
+ exit(0)
111
184
 
112
185
  else
113
- puts 'Usage: rails-active-mcp-server [stdio|http] [--port PORT] [--host HOST]'
114
- puts ' stdio: For Claude Desktop integration'
115
- puts ' http: For HTTP-based integrations'
186
+ puts 'Usage: rails-active-mcp-server [stdio] [options]'
187
+ puts ' stdio: For MCP integration (default)'
188
+ puts ''
189
+ puts 'Options:'
190
+ puts ' --help, -h Show this help message'
116
191
  puts ''
117
192
  puts 'Environment variables:'
118
- puts ' RAILS_MCP_DEBUG=1 Enable debug logging'
193
+ puts ' RAILS_MCP_DEBUG=1 Enable debug logging and disable output redirection'
194
+ puts ''
195
+ puts 'Note: HTTP transport was removed in v2.0.0. Use stdio mode for MCP integration.'
119
196
  exit(1)
120
197
  end