rails-mcp 0.1.0
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 +7 -0
- data/LICENSE +22 -0
- data/README.md +175 -0
- data/lib/rails_mcp/executor.rb +38 -0
- data/lib/rails_mcp/mcp/server.rb +85 -0
- data/lib/rails_mcp/version.rb +5 -0
- data/lib/rails_mcp.rb +7 -0
- metadata +117 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ba176e0f681ca2aded9b387f58c157431daba9643297cc455ddfba82757bd4cd
|
|
4
|
+
data.tar.gz: 23b82ff6a767507ffbe990ea84a0632a5d8528b0d53ad34ff68942068f306a80
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 49aebae4786a931d12b49da05884261f2aa20d81412056aa234833709749f69296b37e3ef116c0326666b3f69395a0a3d64ee00b0accac4b98ddde357c2b43c6
|
|
7
|
+
data.tar.gz: f5f1cc5fdbb077df43b792ffbe2e4b515563da28ea97f60dd7d94eeb3391fc5690b18fb55f5732fd093f323f68d898906126db7284e309ff33b0826b1e74c88b
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# rails-mcp
|
|
2
|
+
|
|
3
|
+
> **Setting this up?** Point your coding agent (Cursor, Claude Code, etc.) at [`llms.txt`](./llms.txt) and ask it to self-configure rails-mcp in your project.
|
|
4
|
+
|
|
5
|
+
A Ruby gem that provides AI assistants with ruby code execution capabilities within the context of existing running application server. Think of it giving AI assistant lighting-speed access to ruby console without the need to write script, reload or restart.
|
|
6
|
+
|
|
7
|
+
Works with Rails, Sinatra, Hanami, Roda, and any other Rack-based framework. The code is executed in your application's context for debugging and investigation.
|
|
8
|
+
|
|
9
|
+
<img src="docs/assets/screen.gif" alt="rails console mcp in cursor" width="400"/>
|
|
10
|
+
|
|
11
|
+
<a href="https://youtu.be/lhhOGq6l42s?si=gE4jfwow2aqAtvvk">YouTube Link</a>
|
|
12
|
+
|
|
13
|
+
## Used at
|
|
14
|
+
|
|
15
|
+
<a href="https://www.apollo.io/"><img src="docs/assets/apollo-logo.jpg" alt="Apollo.io" width="100"/></a>
|
|
16
|
+
|
|
17
|
+
Used at Apollo.io against a Rails codebase with 20k+ Ruby files.
|
|
18
|
+
|
|
19
|
+
## Use cases
|
|
20
|
+
|
|
21
|
+
1. Learn a new codebase or code areas quickly. With your AI client and a running server, you can ask it to research while executing snippets from your actual application code. It effectively acts as an in-loop code-verification block.
|
|
22
|
+
2. Perform quick, preliminary investigations of customer escalations using a read-only copy of the production environment. It can execute your application code, locate models, and run relevant class methods or code paths from the codebase to do preliminary root-cause analysis (RCA). Even better if your application uses an event-sourcing framework (i.e., change logs). The AI client, together with the code-execution capabilities via rails-mcp, can deliver fast preliminary RCAs.
|
|
23
|
+
3. Use it for quick data analytics and export reports as CSV.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
Add this line to your application's Gemfile:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
gem "rails-mcp"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or install locally for development:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Mounting the MCP Server
|
|
42
|
+
|
|
43
|
+
**Rails** (`config/routes.rb`):
|
|
44
|
+
```ruby
|
|
45
|
+
require "rails_mcp/mcp/server"
|
|
46
|
+
|
|
47
|
+
Rails.application.routes.draw do
|
|
48
|
+
mount RailsMcp::MCP::Server.new => "/mcp"
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Rack** (`config.ru`):
|
|
53
|
+
```ruby
|
|
54
|
+
require "rails_mcp"
|
|
55
|
+
require "rails_mcp/mcp/server"
|
|
56
|
+
|
|
57
|
+
map "/mcp" do
|
|
58
|
+
run RailsMcp::MCP::Server.new
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Sinatra**:
|
|
63
|
+
```ruby
|
|
64
|
+
require "rails_mcp/mcp/server"
|
|
65
|
+
|
|
66
|
+
mount RailsMcp::MCP::Server.new, at: "/mcp"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Starting the Server
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Standalone with Rackup
|
|
73
|
+
bundle exec rackup -p 9292
|
|
74
|
+
|
|
75
|
+
# With Rails
|
|
76
|
+
bundle exec rails server
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Environment-scoped mounting
|
|
80
|
+
|
|
81
|
+
This gem ships without authentication — the MCP endpoint will accept any request that reaches it. You are responsible for ensuring it is only mounted where appropriate. A typical Rails setup gates it on environment:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
Rails.application.routes.draw do
|
|
85
|
+
mount RailsMcp::MCP::Server.new => "/mcp" if Rails.env.development?
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
For staging or production access, combine that with network-level restrictions (firewall, VPC, SSH port-forward, VPN) so the endpoint is never reachable from the open internet.
|
|
90
|
+
|
|
91
|
+
## MCP Protocol
|
|
92
|
+
|
|
93
|
+
### Endpoint
|
|
94
|
+
|
|
95
|
+
The MCP server exposes a single JSON-RPC endpoint:
|
|
96
|
+
|
|
97
|
+
- **POST /mcp/rpc** - JSON-RPC request and response
|
|
98
|
+
|
|
99
|
+
### Available Tools
|
|
100
|
+
|
|
101
|
+
**evaluate_ruby_code**
|
|
102
|
+
- Description: Evaluates Ruby code and returns the result with captured stdout/stderr
|
|
103
|
+
- Parameters:
|
|
104
|
+
- `code` (string, required): Ruby code to execute
|
|
105
|
+
|
|
106
|
+
## Connecting AI Assistants
|
|
107
|
+
|
|
108
|
+
### Cursor/ Claude Desktop
|
|
109
|
+
|
|
110
|
+
Add to your MCP client configuration (`.cursor/mcp.json` for Cursor or `claude_desktop_config.json` for Claude Desktop):
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"rails-mcp": {
|
|
116
|
+
"url": "http://localhost:3001/mcp/rpc"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
For Claude Code, add it from the command line:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
claude mcp add rails-mcp --transport http http://localhost:3001/mcp/rpc
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+

|
|
129
|
+
|
|
130
|
+
### Other MCP Clients
|
|
131
|
+
|
|
132
|
+
Any MCP-compatible client can connect to the server by making JSON-RPC requests to the `/mcp/rpc` endpoint.
|
|
133
|
+
|
|
134
|
+
Example with curl:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
curl -X POST "http://localhost:3001/mcp/rpc" \
|
|
138
|
+
-H "Content-Type: application/json" \
|
|
139
|
+
-d '{
|
|
140
|
+
"jsonrpc": "2.0",
|
|
141
|
+
"id": 1,
|
|
142
|
+
"method": "tools/list"
|
|
143
|
+
}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Testing
|
|
147
|
+
|
|
148
|
+
Run the test suite:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bundle exec rspec
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Security Warning
|
|
155
|
+
|
|
156
|
+
⚠️ **This gem executes arbitrary Ruby code.**
|
|
157
|
+
|
|
158
|
+
**Important security considerations:**
|
|
159
|
+
- **No built-in authentication.** Gate the mount on environment (e.g., `if Rails.env.development?`) so the endpoint isn't exposed in production by accident
|
|
160
|
+
- Only use in development environments or secure, isolated production environments
|
|
161
|
+
- Rely on network-level restrictions (firewall, VPC, SSH port-forward, VPN) to limit access
|
|
162
|
+
- Consider running in a sandboxed or containerized environment
|
|
163
|
+
- Monitor and log all code execution requests
|
|
164
|
+
|
|
165
|
+
## Concurrency Note
|
|
166
|
+
|
|
167
|
+
This gem uses global `$stdout/$stderr` redirection during evaluation, which can clash in multi-threaded servers. For production use with concurrency, consider:
|
|
168
|
+
- Running in a single worker/thread mode
|
|
169
|
+
- Isolating evaluation per request (e.g., via `fork`)
|
|
170
|
+
- Using a dedicated job worker for code execution
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT License - see LICENSE file for details.
|
|
175
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
require "stringio"
|
|
3
|
+
|
|
4
|
+
module RailsMcp
|
|
5
|
+
class Executor
|
|
6
|
+
# Evaluates Ruby code and captures stdout/stderr.
|
|
7
|
+
def self.eval(code)
|
|
8
|
+
code = code.to_s
|
|
9
|
+
raise ArgumentError, "code can't be blank" if code.strip.empty?
|
|
10
|
+
|
|
11
|
+
out, err = StringIO.new, StringIO.new
|
|
12
|
+
old_out, old_err = $stdout, $stderr
|
|
13
|
+
result = nil
|
|
14
|
+
error = nil
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
$stdout, $stderr = out, err
|
|
18
|
+
result = TOPLEVEL_BINDING.eval(code)
|
|
19
|
+
rescue SyntaxError, StandardError => e
|
|
20
|
+
error = e
|
|
21
|
+
ensure
|
|
22
|
+
$stdout, $stderr = old_out, old_err
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if error
|
|
26
|
+
backtrace = error.backtrace&.first(3)&.join("\n ")
|
|
27
|
+
return "#{error.class}: #{error.message}\n#{backtrace ? " #{backtrace}\n" : ""}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
parts = []
|
|
31
|
+
parts << "=> #{result.inspect}\n" unless result.nil?
|
|
32
|
+
parts << out.string unless out.string.empty?
|
|
33
|
+
parts << err.string unless err.string.empty?
|
|
34
|
+
parts.join
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
require "rack"
|
|
4
|
+
require_relative "../version"
|
|
5
|
+
require_relative "../executor"
|
|
6
|
+
|
|
7
|
+
module RailsMcp
|
|
8
|
+
module MCP
|
|
9
|
+
# Minimal MCP-over-HTTP (JSON-RPC) server.
|
|
10
|
+
# Single-session, single-process demo: fine for local/dev usage.
|
|
11
|
+
class Server
|
|
12
|
+
def initialize
|
|
13
|
+
@tools = [
|
|
14
|
+
{
|
|
15
|
+
"name" => "evaluate_ruby_code",
|
|
16
|
+
"description" => "Evaluate the Ruby code in the context of the current application, returns the result of the code execution",
|
|
17
|
+
"inputSchema" => {
|
|
18
|
+
"type" => "object",
|
|
19
|
+
"properties" => {
|
|
20
|
+
"code" => {
|
|
21
|
+
"type" => "string",
|
|
22
|
+
"description" => "The Ruby code to evaluate, must be a valid Ruby expression or statement"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required" => ["code"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def call(env)
|
|
32
|
+
req = Rack::Request.new(env)
|
|
33
|
+
|
|
34
|
+
case [req.request_method, req.path_info]
|
|
35
|
+
when ["POST", "/rpc"] then handle_rpc(req)
|
|
36
|
+
else
|
|
37
|
+
[404, {"content-type" => "text/plain"}, ["Not Found\n"]]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def handle_rpc(req)
|
|
44
|
+
payload = JSON.parse(req.body.read)
|
|
45
|
+
id = payload["id"]
|
|
46
|
+
method = payload["method"]
|
|
47
|
+
|
|
48
|
+
result =
|
|
49
|
+
case method
|
|
50
|
+
when "initialize"
|
|
51
|
+
{
|
|
52
|
+
"protocolVersion" => "2025-06-18",
|
|
53
|
+
"serverInfo" => { "name" => "rails_mcp", "version" => RailsMcp::VERSION },
|
|
54
|
+
"capabilities" => { "tools" => {} }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
when "tools/list"
|
|
58
|
+
{ "tools" => @tools }
|
|
59
|
+
|
|
60
|
+
when "tools/call"
|
|
61
|
+
name = payload.dig("params", "name")
|
|
62
|
+
args = payload.dig("params", "arguments") || {}
|
|
63
|
+
|
|
64
|
+
if name == "evaluate_ruby_code"
|
|
65
|
+
out = RailsMcp::Executor.eval(args["code"].to_s)
|
|
66
|
+
{ "content" => [{ "type" => "text", "text" => out }] }
|
|
67
|
+
else
|
|
68
|
+
raise "Unknown tool: #{name}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
else
|
|
72
|
+
raise "Unknown method: #{method}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Return JSON-RPC response directly
|
|
76
|
+
response = { "jsonrpc" => "2.0", "id" => id, "result" => result }
|
|
77
|
+
[200, {"content-type" => "application/json"}, [JSON.generate(response)]]
|
|
78
|
+
rescue => e
|
|
79
|
+
response = { "jsonrpc" => "2.0", "id" => id, "error" => { "code" => -32601, "message" => e.message } }
|
|
80
|
+
[200, {"content-type" => "application/json"}, [JSON.generate(response)]]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
data/lib/rails_mcp.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails-mcp
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Raja Jamwal
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rack
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rack-test
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rackup
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: webrick
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
description: A Rack-based MCP server that enables AI assistants to execute Ruby code
|
|
83
|
+
via the Model Context Protocol.
|
|
84
|
+
email:
|
|
85
|
+
- linux.experi@gmail.com
|
|
86
|
+
executables: []
|
|
87
|
+
extensions: []
|
|
88
|
+
extra_rdoc_files: []
|
|
89
|
+
files:
|
|
90
|
+
- LICENSE
|
|
91
|
+
- README.md
|
|
92
|
+
- lib/rails_mcp.rb
|
|
93
|
+
- lib/rails_mcp/executor.rb
|
|
94
|
+
- lib/rails_mcp/mcp/server.rb
|
|
95
|
+
- lib/rails_mcp/version.rb
|
|
96
|
+
homepage: https://github.com/raja-jamwal/rails-mcp
|
|
97
|
+
licenses:
|
|
98
|
+
- MIT
|
|
99
|
+
metadata: {}
|
|
100
|
+
rdoc_options: []
|
|
101
|
+
require_paths:
|
|
102
|
+
- lib
|
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: 2.7.0
|
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '0'
|
|
113
|
+
requirements: []
|
|
114
|
+
rubygems_version: 3.7.2
|
|
115
|
+
specification_version: 4
|
|
116
|
+
summary: Model Context Protocol (MCP) server for Ruby code execution
|
|
117
|
+
test_files: []
|