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 +4 -4
- data/README.md +112 -21
- data/docs/DEBUGGING.md +313 -0
- data/exe/rails-active-mcp-server +39 -13
- data/lib/generators/rails_active_mcp/install/templates/README.md +69 -8
- 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: 695c22b21a980b527c91e1c4d269e7ea4dc260668c2d7ee9eb135d734db95ab3
|
4
|
+
data.tar.gz: 34ff366ef633aef419c388ec77a3dbb768bf8b05f3a915fcf983080ff503540e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
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
|
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
|
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
|
-
####
|
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
|
-
####
|
265
|
+
#### Adding Custom Tools
|
175
266
|
|
176
|
-
|
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.
|
data/exe/rails-active-mcp-server
CHANGED
@@ -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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
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
|
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.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
|