rails-active-mcp 0.1.2 → 0.1.4
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 +126 -21
- data/docs/DEBUGGING.md +313 -0
- data/exe/rails-active-mcp-server +62 -17
- data/lib/generators/rails_active_mcp/install/templates/README.md +98 -10
- data/lib/generators/rails_active_mcp/install/templates/initializer.rb +7 -3
- data/lib/rails_active_mcp/configuration.rb +34 -10
- data/lib/rails_active_mcp/stdio_server.rb +467 -0
- data/lib/rails_active_mcp/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38778186d51ca223866b9cdf181cbe389957c840609934897547d11af69c150c
|
4
|
+
data.tar.gz: 9ed227e53b104de6e482428a37837b80dd0b30f01405d3abaab8e12c4e6175c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e80bc1548f85c52f88e766a273a7f6a3f4637a398b549a36dd65b756a4243a2d86511354324120938aabdd40c5427abe9be12169890b7745721847f6da2dbfa4
|
7
|
+
data.tar.gz: fd5511135a658f588d154526bbcbe1da8b2d75b7c13d3c73687aba726293555cbd1b3f38defbe8f51ea1ce33fef3c05b38fb82c7e1d8d8f9fb96f6e214d742b2
|
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
|
|
@@ -51,6 +51,11 @@ RailsActiveMcp.configure do |config|
|
|
51
51
|
config.default_timeout = 30
|
52
52
|
config.max_results = 100
|
53
53
|
|
54
|
+
# Server configuration
|
55
|
+
config.server_mode = :stdio # :stdio for Claude Desktop, :http for web
|
56
|
+
config.server_host = 'localhost'
|
57
|
+
config.server_port = 3001
|
58
|
+
|
54
59
|
# Model access control
|
55
60
|
config.allowed_models = %w[User Post Comment] # Empty = all allowed
|
56
61
|
config.blocked_models = %w[AdminUser Secret]
|
@@ -64,6 +69,10 @@ RailsActiveMcp.configure do |config|
|
|
64
69
|
config.production_mode! # Very strict
|
65
70
|
config.strict_mode! # Safe defaults
|
66
71
|
config.permissive_mode! # Development friendly
|
72
|
+
|
73
|
+
# Server mode shortcuts
|
74
|
+
config.stdio_mode! # Set stdio mode for Claude Desktop
|
75
|
+
config.http_mode!(host: '0.0.0.0', port: 8080) # Set HTTP mode with custom host/port
|
67
76
|
end
|
68
77
|
```
|
69
78
|
|
@@ -71,24 +80,37 @@ end
|
|
71
80
|
|
72
81
|
You have several options for running the MCP server:
|
73
82
|
|
74
|
-
### Option 1:
|
83
|
+
### Option 1: Use configured mode (recommended)
|
84
|
+
|
85
|
+
```bash
|
86
|
+
$ bundle exec rails-active-mcp-server
|
87
|
+
```
|
88
|
+
|
89
|
+
This will use the server mode configured in your initializer (`:stdio` by default). You can override the mode:
|
90
|
+
|
91
|
+
```bash
|
92
|
+
$ bundle exec rails-active-mcp-server stdio # Force stdio mode
|
93
|
+
$ bundle exec rails-active-mcp-server http # Force HTTP mode
|
94
|
+
```
|
95
|
+
|
96
|
+
### Option 2: Rails-mounted (HTTP, good for development)
|
75
97
|
|
76
98
|
```bash
|
77
99
|
$ rails server
|
78
100
|
# MCP server available at http://localhost:3000/mcp
|
79
101
|
```
|
80
102
|
|
81
|
-
### Option
|
103
|
+
### Option 3: Standalone HTTP server
|
82
104
|
|
83
105
|
```bash
|
84
|
-
$ bundle exec rails-active-mcp-server
|
106
|
+
$ bundle exec rails-active-mcp-server http
|
85
107
|
# Default: http://localhost:3001
|
86
108
|
|
87
109
|
# Custom host/port
|
88
|
-
$ bundle exec rails-active-mcp-server --host 0.0.0.0 --port 8080
|
110
|
+
$ bundle exec rails-active-mcp-server http --host 0.0.0.0 --port 8080
|
89
111
|
```
|
90
112
|
|
91
|
-
### Option
|
113
|
+
### Option 4: Using rackup
|
92
114
|
|
93
115
|
```bash
|
94
116
|
$ rackup mcp.ru -p 3001
|
@@ -98,7 +120,48 @@ $ rackup mcp.ru -p 3001
|
|
98
120
|
|
99
121
|
### With MCP Clients
|
100
122
|
|
101
|
-
####
|
123
|
+
#### Claude Desktop Integration (Preferred)
|
124
|
+
|
125
|
+
Add to your Claude Desktop configuration file:
|
126
|
+
|
127
|
+
**Location:**
|
128
|
+
- macOS/Linux: `~/.config/claude-desktop/claude_desktop_config.json`
|
129
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
130
|
+
|
131
|
+
```json
|
132
|
+
{
|
133
|
+
"mcpServers": {
|
134
|
+
"rails-active-mcp": {
|
135
|
+
"command": "bundle",
|
136
|
+
"args": ["exec", "rails-active-mcp-server", "stdio"],
|
137
|
+
"cwd": "/path/to/your/rails/project"
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
```
|
142
|
+
|
143
|
+
Or if installed globally:
|
144
|
+
|
145
|
+
```json
|
146
|
+
{
|
147
|
+
"mcpServers": {
|
148
|
+
"rails-active-mcp": {
|
149
|
+
"command": "rails-active-mcp-server",
|
150
|
+
"args": ["stdio"],
|
151
|
+
"cwd": "/path/to/your/rails/project"
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
Then in Claude Desktop, you can use prompts like:
|
158
|
+
|
159
|
+
- "Show me all users created in the last week"
|
160
|
+
- "What's the average order value?"
|
161
|
+
- "Check the User model schema and associations"
|
162
|
+
- "Analyze this code for safety: User.delete_all"
|
163
|
+
|
164
|
+
#### Warp Terminal Integration (HTTP)
|
102
165
|
|
103
166
|
Add to your Warp MCP configuration:
|
104
167
|
|
@@ -120,16 +183,6 @@ Add to your Warp MCP configuration:
|
|
120
183
|
}
|
121
184
|
```
|
122
185
|
|
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
186
|
#### Custom MCP Clients
|
134
187
|
|
135
188
|
The server implements the MCP protocol (JSONRPC 2.0). Connect any MCP-compatible client to:
|
@@ -154,9 +207,11 @@ puts analysis[:estimated_risk] # => :critical
|
|
154
207
|
|
155
208
|
### Available MCP Tools
|
156
209
|
|
210
|
+
The Rails Active MCP server provides several built-in tools that will appear in Claude Desktop:
|
211
|
+
|
157
212
|
#### `rails_console_execute`
|
158
213
|
|
159
|
-
Execute Ruby code with safety checks:
|
214
|
+
Execute Ruby code with safety checks and timeout protection:
|
160
215
|
|
161
216
|
```json
|
162
217
|
{
|
@@ -165,15 +220,65 @@ Execute Ruby code with safety checks:
|
|
165
220
|
"name": "rails_console_execute",
|
166
221
|
"arguments": {
|
167
222
|
"code": "User.where(active: true).count",
|
168
|
-
"timeout": 30
|
223
|
+
"timeout": 30,
|
224
|
+
"safe_mode": true
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
```
|
229
|
+
|
230
|
+
#### `rails_model_info`
|
231
|
+
|
232
|
+
Get detailed information about Rails models:
|
233
|
+
|
234
|
+
```json
|
235
|
+
{
|
236
|
+
"method": "tools/call",
|
237
|
+
"params": {
|
238
|
+
"name": "rails_model_info",
|
239
|
+
"arguments": {
|
240
|
+
"model_name": "User"
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
```
|
245
|
+
|
246
|
+
#### `rails_safe_query`
|
247
|
+
|
248
|
+
Execute safe, read-only database queries:
|
249
|
+
|
250
|
+
```json
|
251
|
+
{
|
252
|
+
"method": "tools/call",
|
253
|
+
"params": {
|
254
|
+
"name": "rails_safe_query",
|
255
|
+
"arguments": {
|
256
|
+
"query": "where(active: true).count",
|
257
|
+
"model": "User"
|
258
|
+
}
|
259
|
+
}
|
260
|
+
}
|
261
|
+
```
|
262
|
+
|
263
|
+
#### `rails_dry_run`
|
264
|
+
|
265
|
+
Analyze Ruby code for safety without executing:
|
266
|
+
|
267
|
+
```json
|
268
|
+
{
|
269
|
+
"method": "tools/call",
|
270
|
+
"params": {
|
271
|
+
"name": "rails_dry_run",
|
272
|
+
"arguments": {
|
273
|
+
"code": "User.delete_all"
|
169
274
|
}
|
170
275
|
}
|
171
276
|
}
|
172
277
|
```
|
173
278
|
|
174
|
-
####
|
279
|
+
#### Adding Custom Tools
|
175
280
|
|
176
|
-
|
281
|
+
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
282
|
|
178
283
|
```ruby
|
179
284
|
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.
|
data/exe/rails-active-mcp-server
CHANGED
@@ -3,22 +3,67 @@
|
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
# Load Rails configuration if available
|
10
|
+
require_relative '../config/environment' if File.exist?('config/environment.rb')
|
11
|
+
|
12
|
+
# Parse command line options with config defaults
|
13
|
+
default_mode = if defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config)
|
14
|
+
RailsActiveMcp.config.server_mode.to_s
|
15
|
+
else
|
16
|
+
'stdio'
|
17
|
+
end
|
18
|
+
default_port = if defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config)
|
19
|
+
RailsActiveMcp.config.server_port
|
20
|
+
else
|
21
|
+
3001
|
22
|
+
end
|
23
|
+
default_host = if defined?(RailsActiveMcp) && RailsActiveMcp.respond_to?(:config)
|
24
|
+
RailsActiveMcp.config.server_host
|
25
|
+
else
|
26
|
+
'localhost'
|
27
|
+
end
|
28
|
+
|
29
|
+
transport = ARGV[0] || default_mode
|
30
|
+
port = ARGV.include?('--port') ? ARGV[ARGV.index('--port') + 1].to_i : default_port
|
31
|
+
host = ARGV.include?('--host') ? ARGV[ARGV.index('--host') + 1] : default_host
|
32
|
+
|
33
|
+
case transport
|
34
|
+
when 'stdio'
|
35
|
+
# Stdio transport for Claude Desktop
|
36
|
+
require_relative '../lib/rails_active_mcp/stdio_server'
|
37
|
+
|
38
|
+
begin
|
39
|
+
# Initialize Rails environment if available
|
40
|
+
require_relative '../config/environment' if File.exist?('config/environment.rb')
|
41
|
+
|
42
|
+
stdio_server = RailsActiveMcp::StdioServer.new
|
43
|
+
stdio_server.run
|
44
|
+
rescue Interrupt
|
45
|
+
exit(0)
|
46
|
+
end
|
47
|
+
|
48
|
+
when 'http'
|
49
|
+
# HTTP transport for other integrations
|
50
|
+
puts "Starting Rails Active MCP Server on #{host}:#{port}"
|
51
|
+
puts 'Press Ctrl+C to stop'
|
52
|
+
|
53
|
+
begin
|
54
|
+
Rack::Handler::WEBrick.run(
|
55
|
+
RailsActiveMcp::McpServer.new,
|
56
|
+
Port: port,
|
57
|
+
Host: host,
|
58
|
+
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN)
|
59
|
+
)
|
60
|
+
rescue Interrupt
|
61
|
+
puts "\nShutting down server..."
|
62
|
+
end
|
63
|
+
|
64
|
+
else
|
65
|
+
puts 'Usage: rails-active-mcp-server [stdio|http] [--port PORT] [--host HOST]'
|
66
|
+
puts ' stdio: For Claude Desktop integration'
|
67
|
+
puts ' http: For HTTP-based integrations (default)'
|
68
|
+
exit(1)
|
69
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
================================================================================
|
3
2
|
Rails Active MCP has been installed!
|
4
3
|
================================================================================
|
@@ -13,17 +12,54 @@ Next Steps:
|
|
13
12
|
|
14
13
|
1. Review and customize the configuration in config/initializers/rails_active_mcp.rb
|
15
14
|
|
16
|
-
2.
|
17
|
-
|
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
20
|
|
20
|
-
|
21
|
+
3. Start the MCP server:
|
22
|
+
Option A: Use configured mode (RECOMMENDED)
|
21
23
|
$ bundle exec rails-active-mcp-server
|
22
24
|
|
23
|
-
Option
|
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
|
24
33
|
$ rackup mcp.ru -p 3001
|
25
34
|
|
26
|
-
|
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)
|
39
|
+
|
40
|
+
{
|
41
|
+
"mcpServers": {
|
42
|
+
"rails-active-mcp": {
|
43
|
+
"command": "bundle",
|
44
|
+
"args": ["exec", "rails-active-mcp-server", "stdio"],
|
45
|
+
"cwd": "/path/to/your/rails/project"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
OR if you've installed the gem globally:
|
51
|
+
|
52
|
+
{
|
53
|
+
"mcpServers": {
|
54
|
+
"rails-active-mcp": {
|
55
|
+
"command": "rails-active-mcp-server",
|
56
|
+
"args": ["stdio"],
|
57
|
+
"cwd": "/path/to/your/rails/project"
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
5. For other MCP clients (HTTP-based), add this to your MCP configuration:
|
27
63
|
{
|
28
64
|
"mcpServers": {
|
29
65
|
"rails-console": {
|
@@ -33,13 +69,16 @@ Next Steps:
|
|
33
69
|
}
|
34
70
|
}
|
35
71
|
|
36
|
-
|
72
|
+
6. Test the installation:
|
37
73
|
$ rails console
|
38
74
|
> RailsActiveMcp.safe?("User.count")
|
39
75
|
> RailsActiveMcp.execute("User.count")
|
40
76
|
|
41
|
-
Built-in Tools:
|
42
|
-
- rails_console_execute: Execute Ruby code with safety checks
|
77
|
+
Built-in Tools Available in Claude:
|
78
|
+
- rails_console_execute: Execute Ruby code in Rails console with safety checks
|
79
|
+
- rails_model_info: Get detailed information about Rails models
|
80
|
+
- rails_safe_query: Execute safe read-only database queries
|
81
|
+
- rails_dry_run: Analyze Ruby code for safety without execution
|
43
82
|
|
44
83
|
Extend with custom tools by modifying the MCP server implementation.
|
45
84
|
|
@@ -49,11 +88,60 @@ Security Notes:
|
|
49
88
|
- Dangerous operations are blocked in safe mode
|
50
89
|
- Review the safety patterns in the configuration
|
51
90
|
|
91
|
+
Transport Modes:
|
92
|
+
- stdio: For Claude Desktop and compatible MCP clients (recommended)
|
93
|
+
- http: For HTTP-based integrations and web applications
|
94
|
+
|
95
|
+
Configuration Examples:
|
96
|
+
|
97
|
+
For Claude Desktop (default):
|
98
|
+
config.server_mode = :stdio
|
99
|
+
|
100
|
+
For HTTP web integrations:
|
101
|
+
config.server_mode = :http
|
102
|
+
config.server_host = 'localhost'
|
103
|
+
config.server_port = 3001
|
104
|
+
|
105
|
+
For Docker/remote access:
|
106
|
+
config.http_mode!(host: '0.0.0.0', port: 8080)
|
107
|
+
|
108
|
+
For development with multiple transport modes:
|
109
|
+
if Rails.env.development?
|
110
|
+
config.stdio_mode! # Claude Desktop
|
111
|
+
else
|
112
|
+
config.http_mode!(host: '0.0.0.0', port: 3001) # Production HTTP
|
113
|
+
end
|
114
|
+
|
52
115
|
Custom MCP Server Benefits:
|
53
116
|
- No external dependencies
|
54
117
|
- Full control over implementation
|
55
118
|
- Simplified deployment
|
56
119
|
- Enhanced security
|
120
|
+
- Works with Claude Desktop
|
121
|
+
|
122
|
+
Debugging and Troubleshooting:
|
123
|
+
|
124
|
+
For interactive debugging, use the MCP Inspector:
|
125
|
+
$ bin/debug-mcp-server --mode inspector
|
126
|
+
|
127
|
+
This will:
|
128
|
+
- Launch the MCP Inspector connected to your server
|
129
|
+
- Allow interactive testing of all tools
|
130
|
+
- Show real-time debug output and logs
|
131
|
+
|
132
|
+
Debug logging:
|
133
|
+
$ RAILS_MCP_DEBUG=1 bundle exec rails-active-mcp-server stdio
|
134
|
+
|
135
|
+
View Claude Desktop logs:
|
136
|
+
$ tail -f ~/Library/Logs/Claude/mcp*.log # macOS
|
137
|
+
$ tail -f ~/.config/claude-desktop/logs/*.log # Linux
|
138
|
+
|
139
|
+
Common issues:
|
140
|
+
- Ensure your Rails environment loads properly in the project directory
|
141
|
+
- Check that the gem is properly installed and configured
|
142
|
+
- Verify the Rails application starts without errors
|
143
|
+
- Make sure the cwd path in Claude Desktop config is correct
|
144
|
+
- Enable debug logging with RAILS_MCP_DEBUG=1 for detailed output
|
57
145
|
|
58
146
|
For more information: https://github.com/goodpie/rails-active-mcp
|
59
147
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rails_active_mcp'
|
2
2
|
|
3
|
-
|
4
3
|
RailsActiveMcp.configure do |config|
|
5
4
|
# Enable/disable the MCP server
|
6
5
|
config.enabled = true
|
@@ -19,7 +18,12 @@ RailsActiveMcp.configure do |config|
|
|
19
18
|
|
20
19
|
# Logging and auditing
|
21
20
|
config.log_executions = true
|
22
|
-
config.audit_file = Rails.root.join(
|
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
|
23
27
|
|
24
28
|
# Environment-specific settings
|
25
29
|
case Rails.env
|
@@ -35,5 +39,5 @@ RailsActiveMcp.configure do |config|
|
|
35
39
|
# config.add_safety_pattern(/CustomDangerousMethod/, "Custom dangerous operation")
|
36
40
|
|
37
41
|
# Operations that require manual confirmation
|
38
|
-
config.require_confirmation_for = [
|
42
|
+
config.require_confirmation_for = %i[delete destroy update_all delete_all]
|
39
43
|
end
|
@@ -5,7 +5,8 @@ module RailsActiveMcp
|
|
5
5
|
attr_accessor :enabled, :safe_mode, :default_timeout, :max_results,
|
6
6
|
:allowed_models, :blocked_models, :custom_safety_patterns,
|
7
7
|
:log_executions, :audit_file, :enable_mutation_tools,
|
8
|
-
:require_confirmation_for, :execution_environment
|
8
|
+
:require_confirmation_for, :execution_environment, :server_mode,
|
9
|
+
:server_host, :server_port
|
9
10
|
|
10
11
|
def initialize
|
11
12
|
@enabled = true
|
@@ -17,10 +18,16 @@ module RailsActiveMcp
|
|
17
18
|
@custom_safety_patterns = []
|
18
19
|
@log_executions = true
|
19
20
|
# Safe Rails.root access
|
20
|
-
|
21
|
+
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
22
|
+
@audit_file = rails_root_join('log',
|
23
|
+
'rails_active_mcp.log')
|
24
|
+
end
|
21
25
|
@enable_mutation_tools = false
|
22
|
-
@require_confirmation_for = [
|
26
|
+
@require_confirmation_for = %i[delete destroy update_all delete_all]
|
23
27
|
@execution_environment = :current # :current, :sandbox, :readonly_replica
|
28
|
+
@server_mode = :stdio # :stdio, :http
|
29
|
+
@server_host = 'localhost'
|
30
|
+
@server_port = 3001
|
24
31
|
end
|
25
32
|
|
26
33
|
# Safety configuration
|
@@ -42,7 +49,7 @@ module RailsActiveMcp
|
|
42
49
|
strict_mode!
|
43
50
|
@execution_environment = :readonly_replica
|
44
51
|
@log_executions = true
|
45
|
-
@require_confirmation_for = [
|
52
|
+
@require_confirmation_for = %i[delete destroy update create save]
|
46
53
|
end
|
47
54
|
|
48
55
|
# Model access configuration
|
@@ -58,6 +65,21 @@ module RailsActiveMcp
|
|
58
65
|
@custom_safety_patterns << { pattern: pattern, description: description }
|
59
66
|
end
|
60
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
|
+
|
61
83
|
# Validation
|
62
84
|
def model_allowed?(model_name)
|
63
85
|
model_str = model_name.to_s
|
@@ -73,13 +95,15 @@ module RailsActiveMcp
|
|
73
95
|
end
|
74
96
|
|
75
97
|
def validate!
|
76
|
-
raise ArgumentError,
|
77
|
-
raise ArgumentError,
|
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
|
78
102
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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)
|
83
107
|
end
|
84
108
|
|
85
109
|
private
|
@@ -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
|
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.
|
4
|
+
version: 0.1.4
|
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
|