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.
- checksums.yaml +4 -4
- data/README.md +106 -279
- data/changelog.md +69 -0
- data/docs/DEBUGGING.md +5 -5
- data/docs/README.md +130 -142
- data/examples/rails_app_integration.md +405 -0
- data/exe/rails-active-mcp-server +153 -76
- data/gemfiles/rails_7.1.gemfile +34 -0
- data/lib/generators/rails_active_mcp/install/install_generator.rb +19 -39
- data/lib/generators/rails_active_mcp/install/templates/README.md +134 -188
- data/lib/generators/rails_active_mcp/install/templates/initializer.rb +65 -28
- data/lib/generators/rails_active_mcp/install/templates/mcp.ru +7 -3
- data/lib/rails_active_mcp/configuration.rb +37 -98
- data/lib/rails_active_mcp/console_executor.rb +13 -3
- data/lib/rails_active_mcp/engine.rb +36 -24
- data/lib/rails_active_mcp/sdk/server.rb +183 -0
- data/lib/rails_active_mcp/sdk/tools/console_execute_tool.rb +103 -0
- data/lib/rails_active_mcp/sdk/tools/dry_run_tool.rb +73 -0
- data/lib/rails_active_mcp/sdk/tools/model_info_tool.rb +106 -0
- data/lib/rails_active_mcp/sdk/tools/safe_query_tool.rb +77 -0
- data/lib/rails_active_mcp/tasks.rake +236 -80
- data/lib/rails_active_mcp/version.rb +1 -1
- data/lib/rails_active_mcp.rb +5 -11
- data/rails_active_mcp.gemspec +62 -11
- metadata +83 -24
- data/app/controllers/rails_active_mcp/mcp_controller.rb +0 -80
- data/lib/rails_active_mcp/mcp_server.rb +0 -383
- data/lib/rails_active_mcp/railtie.rb +0 -70
- data/lib/rails_active_mcp/stdio_server.rb +0 -517
- data/lib/rails_active_mcp/tools/console_execute_tool.rb +0 -61
- data/lib/rails_active_mcp/tools/dry_run_tool.rb +0 -41
- data/lib/rails_active_mcp/tools/model_info_tool.rb +0 -70
- 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.
|
data/exe/rails-active-mcp-server
CHANGED
@@ -1,120 +1,197 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
# Require essential gems first
|
5
5
|
require 'json'
|
6
6
|
require 'stringio'
|
7
7
|
|
8
|
-
#
|
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
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
28
|
-
|
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
|
33
|
-
if
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
82
|
-
|
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
|
-
|
89
|
-
puts '
|
90
|
-
|
91
|
-
puts
|
92
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
114
|
-
puts ' stdio: For
|
115
|
-
puts '
|
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
|