rails-active-mcp 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bec983e79b11a278a9f7c39de53d6eff389f4ebbd951e9a613ce313ea3a48ba2
4
- data.tar.gz: ed94322b6c2bb06cff349166dbdf4d95babeea80ac6ae2c355683c19c0dc682e
3
+ metadata.gz: 695c22b21a980b527c91e1c4d269e7ea4dc260668c2d7ee9eb135d734db95ab3
4
+ data.tar.gz: 34ff366ef633aef419c388ec77a3dbb768bf8b05f3a915fcf983080ff503540e
5
5
  SHA512:
6
- metadata.gz: 2281c14e99c4f2ad588fe0b222e060cb0b04e9bf0b3b874c284d6f6d6db8757a91d46a1aba7a77f365fe45c518d3a9e9bc58e2e2fbd6738c167334adfd252436
7
- data.tar.gz: '0178374d4a7e8c365c582f8cc11ffce5c28d23b3ea8d0d35078cea47eaff5a3d316749ec28b7e724815e293c439b05c989be20187abed0f076d443463da4afbb'
6
+ metadata.gz: 9c659fce767c7f9249f970a40d5fa1c627b151f2ef86990f676f8e50ddf2d9d398772389761e67ced5382b0640d69119be1fddf804b8fe7905814df55b659ec8
7
+ data.tar.gz: 41d93c9415d7352a35fe2e914784a1581c15dfd015e63f964d8b60be46c7334edfa69247f574075c111482ae37aa3e93bf9500441253019f910d9b85cd13a98b
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rails Active MCP
2
2
 
3
- A Ruby gem that provides secure Rails console access through Model Context Protocol (MCP) for AI agents and development tools like Warp Terminal. Built with a custom MCP server implementation for full control and flexibility.
3
+ A Ruby gem that provides secure Rails console access through Model Context Protocol (MCP) for AI agents and development tools like Claude Desktop, Warp Terminal, and other MCP clients. Built with a custom MCP server implementation for full control and flexibility.
4
4
 
5
5
  ## Features
6
6
 
@@ -71,24 +71,32 @@ end
71
71
 
72
72
  You have several options for running the MCP server:
73
73
 
74
- ### Option 1: Rails-mounted (recommended for development)
74
+ ### Option 1: Stdio server (recommended for Claude Desktop)
75
+
76
+ ```bash
77
+ $ bundle exec rails-active-mcp-server stdio
78
+ ```
79
+
80
+ This mode is required for Claude Desktop integration and other MCP clients that use stdio transport.
81
+
82
+ ### Option 2: Rails-mounted (HTTP, good for development)
75
83
 
76
84
  ```bash
77
85
  $ rails server
78
86
  # MCP server available at http://localhost:3000/mcp
79
87
  ```
80
88
 
81
- ### Option 2: Standalone server
89
+ ### Option 3: Standalone HTTP server
82
90
 
83
91
  ```bash
84
- $ bundle exec rails-active-mcp-server
92
+ $ bundle exec rails-active-mcp-server http
85
93
  # Default: http://localhost:3001
86
94
 
87
95
  # Custom host/port
88
- $ bundle exec rails-active-mcp-server --host 0.0.0.0 --port 8080
96
+ $ bundle exec rails-active-mcp-server http --host 0.0.0.0 --port 8080
89
97
  ```
90
98
 
91
- ### Option 3: Using rackup
99
+ ### Option 4: Using rackup
92
100
 
93
101
  ```bash
94
102
  $ rackup mcp.ru -p 3001
@@ -98,7 +106,48 @@ $ rackup mcp.ru -p 3001
98
106
 
99
107
  ### With MCP Clients
100
108
 
101
- #### Warp Terminal Integration
109
+ #### Claude Desktop Integration (Preferred)
110
+
111
+ Add to your Claude Desktop configuration file:
112
+
113
+ **Location:**
114
+ - macOS/Linux: `~/.config/claude-desktop/claude_desktop_config.json`
115
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "rails-active-mcp": {
121
+ "command": "bundle",
122
+ "args": ["exec", "rails-active-mcp-server", "stdio"],
123
+ "cwd": "/path/to/your/rails/project"
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ Or if installed globally:
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "rails-active-mcp": {
135
+ "command": "rails-active-mcp-server",
136
+ "args": ["stdio"],
137
+ "cwd": "/path/to/your/rails/project"
138
+ }
139
+ }
140
+ }
141
+ ```
142
+
143
+ Then in Claude Desktop, you can use prompts like:
144
+
145
+ - "Show me all users created in the last week"
146
+ - "What's the average order value?"
147
+ - "Check the User model schema and associations"
148
+ - "Analyze this code for safety: User.delete_all"
149
+
150
+ #### Warp Terminal Integration (HTTP)
102
151
 
103
152
  Add to your Warp MCP configuration:
104
153
 
@@ -120,16 +169,6 @@ Add to your Warp MCP configuration:
120
169
  }
121
170
  ```
122
171
 
123
- Then in Warp, you can use prompts like:
124
-
125
- - "Show me all users created in the last week"
126
- - "What's the average order value?"
127
- - "Check the User model schema and associations"
128
-
129
- #### Claude Desktop / Cline
130
-
131
- Use the same configuration format as above, pointing to your MCP server.
132
-
133
172
  #### Custom MCP Clients
134
173
 
135
174
  The server implements the MCP protocol (JSONRPC 2.0). Connect any MCP-compatible client to:
@@ -154,9 +193,11 @@ puts analysis[:estimated_risk] # => :critical
154
193
 
155
194
  ### Available MCP Tools
156
195
 
196
+ The Rails Active MCP server provides several built-in tools that will appear in Claude Desktop:
197
+
157
198
  #### `rails_console_execute`
158
199
 
159
- Execute Ruby code with safety checks:
200
+ Execute Ruby code with safety checks and timeout protection:
160
201
 
161
202
  ```json
162
203
  {
@@ -165,15 +206,65 @@ Execute Ruby code with safety checks:
165
206
  "name": "rails_console_execute",
166
207
  "arguments": {
167
208
  "code": "User.where(active: true).count",
168
- "timeout": 30
209
+ "timeout": 30,
210
+ "safe_mode": true
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ #### `rails_model_info`
217
+
218
+ Get detailed information about Rails models:
219
+
220
+ ```json
221
+ {
222
+ "method": "tools/call",
223
+ "params": {
224
+ "name": "rails_model_info",
225
+ "arguments": {
226
+ "model_name": "User"
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ #### `rails_safe_query`
233
+
234
+ Execute safe, read-only database queries:
235
+
236
+ ```json
237
+ {
238
+ "method": "tools/call",
239
+ "params": {
240
+ "name": "rails_safe_query",
241
+ "arguments": {
242
+ "query": "where(active: true).count",
243
+ "model": "User"
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ #### `rails_dry_run`
250
+
251
+ Analyze Ruby code for safety without executing:
252
+
253
+ ```json
254
+ {
255
+ "method": "tools/call",
256
+ "params": {
257
+ "name": "rails_dry_run",
258
+ "arguments": {
259
+ "code": "User.delete_all"
169
260
  }
170
261
  }
171
262
  }
172
263
  ```
173
264
 
174
- #### Additional Tools
265
+ #### Adding Custom Tools
175
266
 
176
- The custom server includes built-in support for the main console execute tool. You can extend the server with additional tools by modifying the `McpServer` class in `lib/rails_active_mcp/mcp_server.rb`:
267
+ You can extend the server with additional tools by modifying the `McpServer` class in `lib/rails_active_mcp/mcp_server.rb` or `lib/rails_active_mcp/stdio_server.rb`:
177
268
 
178
269
  ```ruby
179
270
  def register_default_tools
data/docs/DEBUGGING.md ADDED
@@ -0,0 +1,313 @@
1
+ # Rails Active MCP Debugging Guide
2
+
3
+ This guide covers debugging and troubleshooting for the Rails Active MCP server, following the [MCP debugging best practices](https://modelcontextprotocol.io/docs/tools/debugging).
4
+
5
+ ## Quick Debug Commands
6
+
7
+ ```bash
8
+ # Test with MCP Inspector (recommended)
9
+ bin/debug-mcp-server --mode inspector
10
+
11
+ # Enable debug logging
12
+ RAILS_MCP_DEBUG=1 bundle exec rails-active-mcp-server stdio
13
+
14
+ # View recent logs
15
+ bin/debug-mcp-server --mode logs
16
+
17
+ # Basic connectivity test
18
+ bin/debug-mcp-server --mode test
19
+ ```
20
+
21
+ ## Debug Tools Overview
22
+
23
+ ### 1. MCP Inspector (Interactive Testing)
24
+
25
+ The MCP Inspector provides the best debugging experience for MCP servers:
26
+
27
+ ```bash
28
+ # Launch inspector connected to your Rails MCP server
29
+ bin/debug-mcp-server --mode inspector
30
+
31
+ # Or manually:
32
+ npx @modelcontextprotocol/inspector bundle exec rails-active-mcp-server stdio
33
+ ```
34
+
35
+ The Inspector provides:
36
+ - ✅ Interactive tool testing
37
+ - ✅ Real-time message inspection
38
+ - ✅ Error visualization
39
+ - ✅ Request/response history
40
+ - ✅ Server capability discovery
41
+
42
+ ### 2. Debug Logging
43
+
44
+ Enable detailed logging with the `RAILS_MCP_DEBUG` environment variable:
45
+
46
+ ```bash
47
+ RAILS_MCP_DEBUG=1 bundle exec rails-active-mcp-server stdio
48
+ ```
49
+
50
+ This enables:
51
+ - Request/response logging to stderr
52
+ - Tool execution timing
53
+ - Detailed error stack traces
54
+ - JSON-RPC message debugging
55
+
56
+ ### 3. Claude Desktop Integration Debugging
57
+
58
+ #### Checking Server Status in Claude Desktop
59
+
60
+ 1. Click the 🔌 icon to view connected servers
61
+ 2. Click the "Search and tools" 🔍 icon to view available tools
62
+ 3. Look for these Rails Active MCP tools:
63
+ - `rails_console_execute`
64
+ - `rails_model_info`
65
+ - `rails_safe_query`
66
+ - `rails_dry_run`
67
+
68
+ #### Viewing Claude Desktop Logs
69
+
70
+ ```bash
71
+ # macOS
72
+ tail -f ~/Library/Logs/Claude/mcp*.log
73
+
74
+ # Alternative locations
75
+ ls ~/Library/Logs/Claude/
76
+ ```
77
+
78
+ The logs show:
79
+ - Server connection events
80
+ - Configuration issues
81
+ - Runtime errors
82
+ - Tool execution logs
83
+
84
+ #### Using Chrome DevTools in Claude Desktop
85
+
86
+ 1. Enable developer tools:
87
+ ```bash
88
+ echo '{"allowDevTools": true}' > ~/Library/Application\ Support/Claude/developer_settings.json
89
+ ```
90
+
91
+ 2. Open DevTools: `Command-Option-Shift-i`
92
+
93
+ 3. Use Console and Network panels to inspect:
94
+ - Client-side errors
95
+ - Message payloads
96
+ - Connection timing
97
+
98
+ ## Common Issues and Solutions
99
+
100
+ ### 1. Working Directory Issues
101
+
102
+ **Problem**: Server can't find Rails environment or files.
103
+
104
+ **Solution**: Always use absolute paths in Claude Desktop config:
105
+
106
+ ```json
107
+ {
108
+ "mcpServers": {
109
+ "rails-active-mcp": {
110
+ "command": "bundle",
111
+ "args": ["exec", "rails-active-mcp-server", "stdio"],
112
+ "cwd": "/absolute/path/to/your/rails/project"
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### 2. Environment Variables
119
+
120
+ **Problem**: Rails environment or API keys not available.
121
+
122
+ **Solution**: Specify environment variables in config:
123
+
124
+ ```json
125
+ {
126
+ "mcpServers": {
127
+ "rails-active-mcp": {
128
+ "command": "bundle",
129
+ "args": ["exec", "rails-active-mcp-server", "stdio"],
130
+ "cwd": "/path/to/rails/project",
131
+ "env": {
132
+ "RAILS_ENV": "development",
133
+ "RAILS_MCP_DEBUG": "1"
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### 3. Server Initialization Problems
141
+
142
+ **Common initialization issues:**
143
+
144
+ 1. **Path Issues**
145
+ ```bash
146
+ # Test the command directly
147
+ cd /path/to/rails/project
148
+ bundle exec rails-active-mcp-server stdio
149
+ ```
150
+
151
+ 2. **Rails Environment Issues**
152
+ ```bash
153
+ # Verify Rails loads
154
+ cd /path/to/rails/project
155
+ bundle exec rails runner "puts 'Rails loaded successfully'"
156
+ ```
157
+
158
+ 3. **Permission Problems**
159
+ ```bash
160
+ # Check file permissions
161
+ ls -la exe/rails-active-mcp-server
162
+ chmod +x exe/rails-active-mcp-server
163
+ ```
164
+
165
+ ### 4. Connection Problems
166
+
167
+ **Problem**: Claude Desktop can't connect to server.
168
+
169
+ **Debug steps:**
170
+ 1. Check Claude Desktop logs for errors
171
+ 2. Test with MCP Inspector: `bin/debug-mcp-server --mode inspector`
172
+ 3. Verify server process starts: `ps aux | grep rails-active-mcp`
173
+ 4. Test basic JSON-RPC response:
174
+ ```bash
175
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | bundle exec rails-active-mcp-server stdio
176
+ ```
177
+
178
+ ## Logging Implementation
179
+
180
+ ### Server-Side Logging
181
+
182
+ The Rails Active MCP server implements comprehensive logging:
183
+
184
+ ```ruby
185
+ # Logs to stderr (captured by Claude Desktop)
186
+ @logger = Logger.new(STDERR)
187
+ @logger.level = ENV['RAILS_MCP_DEBUG'] ? Logger::DEBUG : Logger::ERROR
188
+
189
+ # MCP log notifications (sent to client)
190
+ def send_log_notification(level, message)
191
+ notification = {
192
+ jsonrpc: JSONRPC_VERSION,
193
+ method: 'notifications/message',
194
+ params: { level: level, data: message }
195
+ }
196
+ puts notification.to_json
197
+ STDOUT.flush
198
+ end
199
+ ```
200
+
201
+ ### Log Levels and Events
202
+
203
+ - **INFO**: Server startup, tool execution start/completion
204
+ - **ERROR**: Parse errors, tool execution failures, unexpected errors
205
+ - **DEBUG**: Request/response details, execution timing (when `RAILS_MCP_DEBUG=1`)
206
+
207
+ ### Important Events Logged
208
+
209
+ 1. **Initialization**: Server startup, tool registration
210
+ 2. **Tool Execution**: Start time, completion time, success/failure
211
+ 3. **Error Conditions**: Parse errors, execution failures, safety violations
212
+ 4. **Performance**: Execution timing, request processing
213
+
214
+ ## Testing Your Implementation
215
+
216
+ ### Basic Connectivity Test
217
+
218
+ ```bash
219
+ # Test initialize method
220
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | bundle exec rails-active-mcp-server stdio
221
+ ```
222
+
223
+ Expected response:
224
+ ```json
225
+ {
226
+ "jsonrpc":"2.0",
227
+ "id":1,
228
+ "result": {
229
+ "protocolVersion":"2025-06-18",
230
+ "capabilities":{"tools":{},"resources":{}},
231
+ "serverInfo":{"name":"rails-active-mcp","version":"..."}
232
+ }
233
+ }
234
+ ```
235
+
236
+ ### Tools List Test
237
+
238
+ ```bash
239
+ # Test tools/list method
240
+ echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | bundle exec rails-active-mcp-server stdio
241
+ ```
242
+
243
+ Should return all 4 Rails Active MCP tools.
244
+
245
+ ### Tool Execution Test
246
+
247
+ ```bash
248
+ # Test a simple tool call
249
+ echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rails_dry_run","arguments":{"code":"puts \"Hello World\""}}}' | bundle exec rails-active-mcp-server stdio
250
+ ```
251
+
252
+ ## Best Practices
253
+
254
+ ### Development Workflow
255
+
256
+ 1. **Start with Inspector**: Use `bin/debug-mcp-server --mode inspector` for development
257
+ 2. **Enable Debug Logging**: Use `RAILS_MCP_DEBUG=1` during development
258
+ 3. **Test Integration**: Test in Claude Desktop with debug logs enabled
259
+ 4. **Monitor Performance**: Check execution times and resource usage
260
+
261
+ ### Security Considerations When Debugging
262
+
263
+ 1. **Sanitize Logs**: Never log sensitive data (passwords, API keys)
264
+ 2. **Limit Debug Info**: Don't expose internal system details in production
265
+ 3. **Protect Credentials**: Use environment variables, not hardcoded values
266
+
267
+ ### Performance Monitoring
268
+
269
+ - Monitor tool execution times
270
+ - Track memory usage during long operations
271
+ - Log slow queries and operations
272
+ - Monitor connection stability
273
+
274
+ ## Getting Help
275
+
276
+ When reporting issues:
277
+
278
+ 1. **Provide log excerpts** from both server and Claude Desktop
279
+ 2. **Include configuration files** (sanitized)
280
+ 3. **List steps to reproduce** the issue
281
+ 4. **Specify environment details** (Ruby version, Rails version, OS)
282
+
283
+ ### Support Resources
284
+
285
+ - [MCP Inspector Documentation](https://modelcontextprotocol.io/docs/tools/inspector)
286
+ - [MCP Debugging Guide](https://modelcontextprotocol.io/docs/tools/debugging)
287
+ - [GitHub Issues](https://github.com/goodpie/rails-active-mcp/issues)
288
+
289
+ ## Advanced Debugging
290
+
291
+ ### Custom Log Analysis
292
+
293
+ Monitor specific patterns in logs:
294
+
295
+ ```bash
296
+ # Watch for tool executions
297
+ tail -f ~/Library/Logs/Claude/mcp*.log | grep "Executing tool"
298
+
299
+ # Monitor errors only
300
+ tail -f ~/Library/Logs/Claude/mcp*.log | grep "ERROR"
301
+
302
+ # Track performance
303
+ tail -f ~/Library/Logs/Claude/mcp*.log | grep "completed in"
304
+ ```
305
+
306
+ ### Development Tips
307
+
308
+ 1. **Use Inspector First**: Always test changes with MCP Inspector before Claude Desktop
309
+ 2. **Enable All Logging**: Use debug mode to see the full request/response cycle
310
+ 3. **Test Edge Cases**: Invalid inputs, timeouts, concurrent requests
311
+ 4. **Monitor Resource Usage**: Watch memory and CPU during development
312
+
313
+ This debugging guide ensures your Rails Active MCP server works reliably with Claude Desktop and other MCP clients.
@@ -3,22 +3,48 @@
3
3
 
4
4
  require 'rack'
5
5
  require 'rack/handler/webrick'
6
+ require 'json'
6
7
  require_relative '../lib/rails_active_mcp'
7
8
 
8
9
  # Parse command line options
10
+ transport = ARGV[0] || 'http'
9
11
  port = ARGV.include?('--port') ? ARGV[ARGV.index('--port') + 1].to_i : 3001
10
12
  host = ARGV.include?('--host') ? ARGV[ARGV.index('--host') + 1] : 'localhost'
11
13
 
12
- puts "Starting Rails Active MCP Server on #{host}:#{port}"
13
- puts "Press Ctrl+C to stop"
14
-
15
- begin
16
- Rack::Handler::WEBrick.run(
17
- RailsActiveMcp::McpServer.new,
18
- Port: port,
19
- Host: host,
20
- Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN)
21
- )
22
- rescue Interrupt
23
- puts "\nShutting down server..."
24
- end
14
+ case transport
15
+ when 'stdio'
16
+ # Stdio transport for Claude Desktop
17
+ require_relative '../lib/rails_active_mcp/stdio_server'
18
+
19
+ begin
20
+ # Initialize Rails environment if available
21
+ require_relative '../config/environment' if File.exist?('config/environment.rb')
22
+
23
+ stdio_server = RailsActiveMcp::StdioServer.new
24
+ stdio_server.run
25
+ rescue Interrupt
26
+ exit(0)
27
+ end
28
+
29
+ when 'http'
30
+ # HTTP transport for other integrations
31
+ puts "Starting Rails Active MCP Server on #{host}:#{port}"
32
+ puts 'Press Ctrl+C to stop'
33
+
34
+ begin
35
+ Rack::Handler::WEBrick.run(
36
+ RailsActiveMcp::McpServer.new,
37
+ Port: port,
38
+ Host: host,
39
+ Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN)
40
+ )
41
+ rescue Interrupt
42
+ puts "\nShutting down server..."
43
+ end
44
+
45
+ else
46
+ puts 'Usage: rails-active-mcp-server [stdio|http] [--port PORT] [--host HOST]'
47
+ puts ' stdio: For Claude Desktop integration'
48
+ puts ' http: For HTTP-based integrations (default)'
49
+ exit(1)
50
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  ================================================================================
3
2
  Rails Active MCP has been installed!
4
3
  ================================================================================
@@ -14,16 +13,46 @@ Next Steps:
14
13
  1. Review and customize the configuration in config/initializers/rails_active_mcp.rb
15
14
 
16
15
  2. Start the MCP server:
17
- Option A: Rails-mounted server
16
+ Option A: Rails-mounted server (HTTP)
18
17
  $ rails server
19
18
 
20
- Option B: Standalone server
21
- $ bundle exec rails-active-mcp-server
19
+ Option B: Standalone HTTP server
20
+ $ bundle exec rails-active-mcp-server http
22
21
 
23
22
  Option C: Using rackup
24
23
  $ rackup mcp.ru -p 3001
25
24
 
26
- 3. For Warp Terminal integration, add this to your MCP configuration:
25
+ Option D: Stdio server for Claude Desktop (RECOMMENDED)
26
+ $ bundle exec rails-active-mcp-server stdio
27
+
28
+ 3. For Claude Desktop integration, add this to your Claude Desktop configuration:
29
+
30
+ Location: ~/.config/claude-desktop/claude_desktop_config.json (Linux/macOS)
31
+ Location: %APPDATA%\Claude\claude_desktop_config.json (Windows)
32
+
33
+ {
34
+ "mcpServers": {
35
+ "rails-active-mcp": {
36
+ "command": "bundle",
37
+ "args": ["exec", "rails-active-mcp-server", "stdio"],
38
+ "cwd": "/path/to/your/rails/project"
39
+ }
40
+ }
41
+ }
42
+
43
+ OR if you've installed the gem globally:
44
+
45
+ {
46
+ "mcpServers": {
47
+ "rails-active-mcp": {
48
+ "command": "rails-active-mcp-server",
49
+ "args": ["stdio"],
50
+ "cwd": "/path/to/your/rails/project"
51
+ }
52
+ }
53
+ }
54
+
55
+ 4. For other MCP clients (HTTP-based), add this to your MCP configuration:
27
56
  {
28
57
  "mcpServers": {
29
58
  "rails-console": {
@@ -33,13 +62,16 @@ Next Steps:
33
62
  }
34
63
  }
35
64
 
36
- 4. Test the installation:
65
+ 5. Test the installation:
37
66
  $ rails console
38
67
  > RailsActiveMcp.safe?("User.count")
39
68
  > RailsActiveMcp.execute("User.count")
40
69
 
41
- Built-in Tools:
42
- - rails_console_execute: Execute Ruby code with safety checks
70
+ Built-in Tools Available in Claude:
71
+ - rails_console_execute: Execute Ruby code in Rails console with safety checks
72
+ - rails_model_info: Get detailed information about Rails models
73
+ - rails_safe_query: Execute safe read-only database queries
74
+ - rails_dry_run: Analyze Ruby code for safety without execution
43
75
 
44
76
  Extend with custom tools by modifying the MCP server implementation.
45
77
 
@@ -49,11 +81,40 @@ Security Notes:
49
81
  - Dangerous operations are blocked in safe mode
50
82
  - Review the safety patterns in the configuration
51
83
 
84
+ Transport Modes:
85
+ - stdio: For Claude Desktop and compatible MCP clients (recommended)
86
+ - http: For HTTP-based integrations and web applications
87
+
52
88
  Custom MCP Server Benefits:
53
89
  - No external dependencies
54
90
  - Full control over implementation
55
91
  - Simplified deployment
56
92
  - Enhanced security
93
+ - Works with Claude Desktop
94
+
95
+ Debugging and Troubleshooting:
96
+
97
+ For interactive debugging, use the MCP Inspector:
98
+ $ bin/debug-mcp-server --mode inspector
99
+
100
+ This will:
101
+ - Launch the MCP Inspector connected to your server
102
+ - Allow interactive testing of all tools
103
+ - Show real-time debug output and logs
104
+
105
+ Debug logging:
106
+ $ RAILS_MCP_DEBUG=1 bundle exec rails-active-mcp-server stdio
107
+
108
+ View Claude Desktop logs:
109
+ $ tail -f ~/Library/Logs/Claude/mcp*.log # macOS
110
+ $ tail -f ~/.config/claude-desktop/logs/*.log # Linux
111
+
112
+ Common issues:
113
+ - Ensure your Rails environment loads properly in the project directory
114
+ - Check that the gem is properly installed and configured
115
+ - Verify the Rails application starts without errors
116
+ - Make sure the cwd path in Claude Desktop config is correct
117
+ - Enable debug logging with RAILS_MCP_DEBUG=1 for detailed output
57
118
 
58
119
  For more information: https://github.com/goodpie/rails-active-mcp
59
120
 
@@ -0,0 +1,467 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logger'
5
+
6
+ module RailsActiveMcp
7
+ class StdioServer
8
+ JSONRPC_VERSION = '2.0'
9
+ MCP_VERSION = '2025-06-18'
10
+
11
+ def initialize
12
+ @tools = {}
13
+ @logger = Logger.new(STDERR) # Log to stderr to avoid interfering with stdout
14
+ @logger.level = ENV['RAILS_MCP_DEBUG'] ? Logger::DEBUG : Logger::ERROR
15
+ @logger.formatter = proc do |severity, datetime, progname, msg|
16
+ "[#{datetime}] [RAILS-MCP] #{severity}: #{msg}\n"
17
+ end
18
+ register_default_tools
19
+ @logger.info "Rails Active MCP Server initialized with #{@tools.size} tools"
20
+ end
21
+
22
+ def run
23
+ @logger.info 'Starting Rails Active MCP Stdio Server'
24
+ send_log_notification('info', 'Rails Active MCP Server started successfully')
25
+
26
+ STDIN.each_line do |line|
27
+ line = line.strip
28
+ next if line.empty?
29
+
30
+ @logger.debug "Received request: #{line}" if ENV['RAILS_MCP_DEBUG']
31
+ data = JSON.parse(line)
32
+
33
+ @logger.debug "Processing method: #{data['method']}" if ENV['RAILS_MCP_DEBUG']
34
+ response = handle_jsonrpc_request(data)
35
+
36
+ if response
37
+ @logger.debug "Sending response: #{response.to_json}" if ENV['RAILS_MCP_DEBUG']
38
+ puts response.to_json
39
+ STDOUT.flush
40
+ end
41
+ rescue JSON::ParserError => e
42
+ @logger.error "JSON Parse Error: #{e.message}"
43
+ send_log_notification('error', "JSON Parse Error: #{e.message}")
44
+ error_response = jsonrpc_error(nil, -32_700, 'Parse error')
45
+ puts error_response.to_json
46
+ STDOUT.flush
47
+ rescue StandardError => e
48
+ @logger.error "Unexpected error: #{e.message}"
49
+ @logger.error e.backtrace.join("\n")
50
+ send_log_notification('error', "Server error: #{e.message}")
51
+ error_response = jsonrpc_error(nil, -32_603, 'Internal error')
52
+ puts error_response.to_json
53
+ STDOUT.flush
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def handle_jsonrpc_request(data)
60
+ case data['method']
61
+ when 'initialize'
62
+ handle_initialize(data)
63
+ when 'tools/list'
64
+ handle_tools_list(data)
65
+ when 'tools/call'
66
+ handle_tools_call(data)
67
+ when 'resources/list'
68
+ handle_resources_list(data)
69
+ when 'resources/read'
70
+ handle_resources_read(data)
71
+ when 'ping'
72
+ handle_ping(data)
73
+ else
74
+ jsonrpc_error(data['id'], -32_601, 'Method not found')
75
+ end
76
+ end
77
+
78
+ def handle_initialize(data)
79
+ {
80
+ jsonrpc: JSONRPC_VERSION,
81
+ id: data['id'],
82
+ result: {
83
+ protocolVersion: MCP_VERSION,
84
+ capabilities: {
85
+ tools: {},
86
+ resources: {}
87
+ },
88
+ serverInfo: {
89
+ name: 'rails-active-mcp',
90
+ version: RailsActiveMcp::VERSION
91
+ }
92
+ }
93
+ }
94
+ end
95
+
96
+ def handle_tools_list(data)
97
+ tools_array = @tools.values.map do |tool|
98
+ tool_def = {
99
+ name: tool[:name],
100
+ description: tool[:description],
101
+ inputSchema: tool[:input_schema]
102
+ }
103
+
104
+ # Add annotations if present
105
+ tool_def[:annotations] = tool[:annotations] if tool[:annotations] && !tool[:annotations].empty?
106
+
107
+ tool_def
108
+ end
109
+
110
+ {
111
+ jsonrpc: JSONRPC_VERSION,
112
+ id: data['id'],
113
+ result: { tools: tools_array }
114
+ }
115
+ end
116
+
117
+ def handle_tools_call(data)
118
+ tool_name = data.dig('params', 'name')
119
+ arguments = data.dig('params', 'arguments') || {}
120
+
121
+ tool = @tools[tool_name]
122
+ return jsonrpc_error(data['id'], -32_602, "Tool '#{tool_name}' not found") unless tool
123
+
124
+ @logger.info "Executing tool: #{tool_name}"
125
+ send_log_notification('info', "Executing tool: #{tool_name}")
126
+
127
+ begin
128
+ start_time = Time.now
129
+ result = tool[:handler].call(arguments)
130
+ execution_time = Time.now - start_time
131
+
132
+ @logger.info "Tool #{tool_name} completed in #{execution_time}s"
133
+ send_log_notification('info', "Tool #{tool_name} completed successfully")
134
+
135
+ {
136
+ jsonrpc: JSONRPC_VERSION,
137
+ id: data['id'],
138
+ result: {
139
+ content: [{ type: 'text', text: result.to_s }],
140
+ isError: false
141
+ }
142
+ }
143
+ rescue StandardError => e
144
+ @logger.error "Tool execution error: #{e.message}"
145
+ @logger.error e.backtrace.first(5).join("\n") if ENV['RAILS_MCP_DEBUG']
146
+ send_log_notification('error', "Tool #{tool_name} failed: #{e.message}")
147
+
148
+ {
149
+ jsonrpc: JSONRPC_VERSION,
150
+ id: data['id'],
151
+ result: {
152
+ content: [{ type: 'text', text: "Error: #{e.message}" }],
153
+ isError: true
154
+ }
155
+ }
156
+ end
157
+ end
158
+
159
+ def handle_resources_list(data)
160
+ {
161
+ jsonrpc: JSONRPC_VERSION,
162
+ id: data['id'],
163
+ result: { resources: [] }
164
+ }
165
+ end
166
+
167
+ def handle_resources_read(data)
168
+ {
169
+ jsonrpc: JSONRPC_VERSION,
170
+ id: data['id'],
171
+ result: { contents: [] }
172
+ }
173
+ end
174
+
175
+ def handle_ping(data)
176
+ {
177
+ jsonrpc: JSONRPC_VERSION,
178
+ id: data['id'],
179
+ result: {}
180
+ }
181
+ end
182
+
183
+ def register_tool(name, description, input_schema, annotations = {}, &handler)
184
+ @tools[name] = {
185
+ name: name,
186
+ description: description,
187
+ input_schema: input_schema,
188
+ annotations: annotations,
189
+ handler: handler
190
+ }
191
+ end
192
+
193
+ def register_default_tools
194
+ register_tool(
195
+ 'rails_console_execute',
196
+ 'Execute Ruby code in Rails console context',
197
+ {
198
+ type: 'object',
199
+ properties: {
200
+ code: { type: 'string', description: 'Ruby code to execute' },
201
+ timeout: { type: 'number', description: 'Timeout in seconds', default: 30 },
202
+ safe_mode: { type: 'boolean', description: 'Enable safety checks', default: true },
203
+ capture_output: { type: 'boolean', description: 'Capture console output', default: true }
204
+ },
205
+ required: ['code']
206
+ },
207
+ {
208
+ title: 'Rails Console Executor',
209
+ readOnlyHint: false,
210
+ destructiveHint: true,
211
+ idempotentHint: false,
212
+ openWorldHint: false
213
+ }
214
+ ) do |args|
215
+ execute_console_code(args)
216
+ end
217
+
218
+ register_tool(
219
+ 'rails_model_info',
220
+ 'Get information about Rails models including columns, associations, and table structure',
221
+ {
222
+ type: 'object',
223
+ properties: {
224
+ model_name: { type: 'string', description: 'Name of the Rails model class to inspect' }
225
+ },
226
+ required: ['model_name']
227
+ },
228
+ {
229
+ title: 'Rails Model Inspector',
230
+ readOnlyHint: true,
231
+ destructiveHint: false,
232
+ idempotentHint: true,
233
+ openWorldHint: false
234
+ }
235
+ ) do |args|
236
+ get_model_info(args['model_name'])
237
+ end
238
+
239
+ register_tool(
240
+ 'rails_safe_query',
241
+ 'Execute safe read-only database queries using ActiveRecord',
242
+ {
243
+ type: 'object',
244
+ properties: {
245
+ query: { type: 'string', description: 'ActiveRecord query to execute (read-only methods only)' },
246
+ model: { type: 'string', description: 'Model class name to query against' }
247
+ },
248
+ required: %w[query model]
249
+ },
250
+ {
251
+ title: 'Rails Safe Query Executor',
252
+ readOnlyHint: true,
253
+ destructiveHint: false,
254
+ idempotentHint: true,
255
+ openWorldHint: false
256
+ }
257
+ ) do |args|
258
+ execute_safe_query(args)
259
+ end
260
+
261
+ register_tool(
262
+ 'rails_dry_run',
263
+ 'Analyze Ruby code for safety without executing it',
264
+ {
265
+ type: 'object',
266
+ properties: {
267
+ code: { type: 'string', description: 'Ruby code to analyze for safety and potential issues' }
268
+ },
269
+ required: ['code']
270
+ },
271
+ {
272
+ title: 'Rails Code Safety Analyzer',
273
+ readOnlyHint: true,
274
+ destructiveHint: false,
275
+ idempotentHint: true,
276
+ openWorldHint: false
277
+ }
278
+ ) do |args|
279
+ dry_run_analysis(args['code'])
280
+ end
281
+ end
282
+
283
+ # Tool implementation methods (reused from McpServer)
284
+ def execute_console_code(args)
285
+ unless defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config) && RailsActiveMcp.config.enabled
286
+ return 'Rails Active MCP is disabled. Enable it in your Rails configuration.'
287
+ end
288
+
289
+ executor = RailsActiveMcp::ConsoleExecutor.new(RailsActiveMcp.config)
290
+
291
+ begin
292
+ result = executor.execute(
293
+ args['code'],
294
+ timeout: args['timeout'] || 30,
295
+ safe_mode: args['safe_mode'] != false,
296
+ capture_output: args['capture_output'] != false
297
+ )
298
+
299
+ if result[:success]
300
+ format_success_result(result)
301
+ else
302
+ "Error: #{result[:error]} (#{result[:error_class]})"
303
+ end
304
+ rescue RailsActiveMcp::SafetyError => e
305
+ "Safety check failed: #{e.message}"
306
+ rescue RailsActiveMcp::TimeoutError => e
307
+ "Execution timed out: #{e.message}"
308
+ rescue StandardError => e
309
+ "Execution failed: #{e.message}"
310
+ end
311
+ end
312
+
313
+ def get_model_info(model_name)
314
+ unless defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config) && RailsActiveMcp.config.enabled
315
+ return 'Rails Active MCP is disabled. Enable it in your Rails configuration.'
316
+ end
317
+
318
+ begin
319
+ # Try to load Rails environment if not already loaded
320
+ require_relative '../../../config/environment' if !defined?(Rails) && File.exist?('config/environment.rb')
321
+
322
+ model_class = model_name.constantize
323
+ unless defined?(ActiveRecord) && model_class < ActiveRecord::Base
324
+ return "#{model_name} is not an ActiveRecord model"
325
+ end
326
+
327
+ info = []
328
+ info << "Model: #{model_class.name}"
329
+ info << "Table: #{model_class.table_name}"
330
+ info << "Columns: #{model_class.column_names.join(', ')}"
331
+
332
+ associations = model_class.reflect_on_all_associations.map(&:name)
333
+ info << "Associations: #{associations.any? ? associations.join(', ') : 'None'}"
334
+
335
+ # Add validation info if available
336
+ if model_class.respond_to?(:validators) && model_class.validators.any?
337
+ validations = model_class.validators.map { |v| "#{v.attributes.join(', ')}: #{v.class.name.demodulize}" }.uniq
338
+ info << "Validations: #{validations.join(', ')}"
339
+ end
340
+
341
+ info.join("\n")
342
+ rescue NameError
343
+ "Model '#{model_name}' not found. Make sure the model class exists and is properly defined."
344
+ rescue StandardError => e
345
+ "Error getting model info: #{e.message}"
346
+ end
347
+ end
348
+
349
+ def execute_safe_query(args)
350
+ unless defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config) && RailsActiveMcp.config.enabled
351
+ return 'Rails Active MCP is disabled. Enable it in your Rails configuration.'
352
+ end
353
+
354
+ begin
355
+ # Try to load Rails environment if not already loaded
356
+ require_relative '../../../config/environment' if !defined?(Rails) && File.exist?('config/environment.rb')
357
+
358
+ model_class = args['model'].constantize
359
+ unless defined?(ActiveRecord) && model_class < ActiveRecord::Base
360
+ return "#{args['model']} is not an ActiveRecord model"
361
+ end
362
+
363
+ # Only allow safe read-only methods
364
+ safe_methods = %w[find find_by where select count sum average maximum minimum first last pluck ids exists?
365
+ empty? any? many? include? limit offset order group having joins includes references distinct uniq readonly]
366
+
367
+ # Extract the first method call to validate it's safe
368
+ query_parts = args['query'].split('.')
369
+ query_method = query_parts.first.split('(').first
370
+
371
+ unless safe_methods.include?(query_method)
372
+ return "Unsafe query method: #{query_method}. Only read-only methods are allowed."
373
+ end
374
+
375
+ result = model_class.instance_eval(args['query'])
376
+
377
+ # Format result appropriately
378
+ case result
379
+ when ActiveRecord::Relation
380
+ "Query returned #{result.count} records: #{result.limit(10).pluck(:id).join(', ')}#{result.count > 10 ? '...' : ''}"
381
+ when Array
382
+ "Array with #{result.length} items: #{result.take(5).inspect}#{result.length > 5 ? '...' : ''}"
383
+ else
384
+ result.to_s
385
+ end
386
+ rescue NameError
387
+ "Model '#{args['model']}' not found. Make sure the model class exists and is properly defined."
388
+ rescue StandardError => e
389
+ "Error executing query: #{e.message}"
390
+ end
391
+ end
392
+
393
+ def dry_run_analysis(code)
394
+ unless defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config) && RailsActiveMcp.config.enabled
395
+ return 'Rails Active MCP is disabled. Enable it in your Rails configuration.'
396
+ end
397
+
398
+ executor = RailsActiveMcp::ConsoleExecutor.new(RailsActiveMcp.config)
399
+
400
+ begin
401
+ analysis = executor.dry_run(code)
402
+
403
+ output = []
404
+ output << 'Code Analysis Results:'
405
+ output << "Code: #{analysis[:code]}"
406
+ output << "Safe: #{analysis[:safety_analysis][:safe] ? 'Yes' : 'No'}"
407
+ output << "Read-only: #{analysis[:safety_analysis][:read_only] ? 'Yes' : 'No'}"
408
+ output << "Risk level: #{analysis[:estimated_risk]}"
409
+ output << "Would execute: #{analysis[:would_execute] ? 'Yes' : 'No'}"
410
+ output << "Summary: #{analysis[:safety_analysis][:summary]}"
411
+
412
+ if analysis[:safety_analysis][:violations] && analysis[:safety_analysis][:violations].any?
413
+ output << "\nSafety Violations:"
414
+ analysis[:safety_analysis][:violations].each do |violation|
415
+ output << " - #{violation[:description]} (#{violation[:severity]})"
416
+ end
417
+ end
418
+
419
+ if analysis[:recommendations] && analysis[:recommendations].any?
420
+ output << "\nRecommendations:"
421
+ analysis[:recommendations].each do |rec|
422
+ output << " - #{rec}"
423
+ end
424
+ end
425
+
426
+ output.join("\n")
427
+ rescue StandardError => e
428
+ "Analysis failed: #{e.message}. Make sure the Rails environment is properly loaded."
429
+ end
430
+ end
431
+
432
+ def format_success_result(result)
433
+ output = []
434
+ output << 'Execution Results:'
435
+ output << "Code: #{result[:code]}"
436
+ output << "Result: #{result[:return_value_string] || result[:return_value]}"
437
+ output << "Output: #{result[:output]}" if result[:output] && !result[:output].empty?
438
+ output << "Execution time: #{result[:execution_time]}s" if result[:execution_time]
439
+ output << "Note: #{result[:note]}" if result[:note]
440
+ output.join("\n")
441
+ end
442
+
443
+ def send_log_notification(level, message)
444
+ notification = {
445
+ jsonrpc: JSONRPC_VERSION,
446
+ method: 'notifications/message',
447
+ params: {
448
+ level: level,
449
+ data: message
450
+ }
451
+ }
452
+
453
+ puts notification.to_json
454
+ STDOUT.flush
455
+ rescue StandardError => e
456
+ @logger.error "Failed to send log notification: #{e.message}"
457
+ end
458
+
459
+ def jsonrpc_error(id, code, message)
460
+ {
461
+ jsonrpc: JSONRPC_VERSION,
462
+ id: id,
463
+ error: { code: code, message: message }
464
+ }
465
+ end
466
+ end
467
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsActiveMcp
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-active-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandyn Britton
@@ -193,6 +193,7 @@ files:
193
193
  - ".idea/vcs.xml"
194
194
  - README.md
195
195
  - changelog.md
196
+ - docs/DEBUGGING.md
196
197
  - docs/README.md
197
198
  - exe/rails-active-mcp-server
198
199
  - lib/generators/rails_active_mcp/install/install_generator.rb
@@ -206,6 +207,7 @@ files:
206
207
  - lib/rails_active_mcp/mcp_server.rb
207
208
  - lib/rails_active_mcp/railtie.rb
208
209
  - lib/rails_active_mcp/safety_checker.rb
210
+ - lib/rails_active_mcp/stdio_server.rb
209
211
  - lib/rails_active_mcp/tasks.rake
210
212
  - lib/rails_active_mcp/tools/console_execute_tool.rb
211
213
  - lib/rails_active_mcp/tools/dry_run_tool.rb