rails-active-mcp 0.1.1
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/.idea/.gitignore +8 -0
- data/.idea/discord.xml +7 -0
- data/.idea/modules.xml +8 -0
- data/.idea/rails-active-mcp-gem.iml +111 -0
- data/.idea/vcs.xml +6 -0
- data/README.md +369 -0
- data/changelog.md +59 -0
- data/docs/README.md +185 -0
- data/exe/rails-active-mcp-server +24 -0
- data/lib/generators/rails_active_mcp/install/install_generator.rb +37 -0
- data/lib/generators/rails_active_mcp/install/templates/README.md +60 -0
- data/lib/generators/rails_active_mcp/install/templates/initializer.rb +39 -0
- data/lib/generators/rails_active_mcp/install/templates/mcp.ru +7 -0
- data/lib/rails_active_mcp/configuration.rb +95 -0
- data/lib/rails_active_mcp/console_executor.rb +378 -0
- data/lib/rails_active_mcp/engine.rb +32 -0
- data/lib/rails_active_mcp/mcp_server.rb +374 -0
- data/lib/rails_active_mcp/railtie.rb +48 -0
- data/lib/rails_active_mcp/safety_checker.rb +149 -0
- data/lib/rails_active_mcp/tasks.rake +154 -0
- data/lib/rails_active_mcp/tools/console_execute_tool.rb +61 -0
- data/lib/rails_active_mcp/tools/dry_run_tool.rb +41 -0
- data/lib/rails_active_mcp/tools/model_info_tool.rb +70 -0
- data/lib/rails_active_mcp/tools/safe_query_tool.rb +41 -0
- data/lib/rails_active_mcp/version.rb +5 -0
- data/lib/rails_active_mcp.rb +59 -0
- data/mcp.ru +5 -0
- data/rails_active_mcp.gemspec +49 -0
- metadata +241 -0
data/docs/README.md
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# Rails Active MCP Documentation
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
Rails Active MCP is a Ruby gem that integrates the Model Context Protocol (MCP) into Rails applications. It provides a secure, configurable, and extensible server for AI agents and developer tools to interact with your Rails app via a standardized protocol.
|
5
|
+
|
6
|
+
## How It Works
|
7
|
+
- **Custom MCP Server**: Implements the MCP protocol (JSON-RPC 2.0 over HTTP) as a Rack middleware, mountable in Rails or runnable standalone.
|
8
|
+
- **Tooling**: Exposes a set of tools (e.g., safe console execution, model info, safe queries, code analysis) to MCP clients.
|
9
|
+
- **Safety**: Advanced safety checks, read-only modes, and audit logging protect your application from dangerous operations.
|
10
|
+
- **Integration**: Easily added to any Rails 7+ app via a generator, with configuration via an initializer.
|
11
|
+
|
12
|
+
## MCP Protocol Overview
|
13
|
+
- **MCP**: The Model Context Protocol is a standard for structured, secure, and auditable access to application internals for AI agents and developer tools.
|
14
|
+
- **Supported Methods**:
|
15
|
+
- `initialize`: Returns server capabilities and protocol version.
|
16
|
+
- `tools/list`: Lists available tools and their schemas.
|
17
|
+
- `tools/call`: Executes a tool with given arguments.
|
18
|
+
- `resources/list` and `resources/read`: (Stubbed, for future resource access.)
|
19
|
+
- **JSON-RPC 2.0**: All communication uses JSON-RPC 2.0 over HTTP POST.
|
20
|
+
|
21
|
+
## Installation & Setup
|
22
|
+
1. Add to your Gemfile:
|
23
|
+
```ruby
|
24
|
+
gem 'rails-active-mcp'
|
25
|
+
```
|
26
|
+
2. Run:
|
27
|
+
```bash
|
28
|
+
bundle install
|
29
|
+
rails generate rails_active_mcp:install
|
30
|
+
```
|
31
|
+
This creates an initializer, mounts the MCP server at `/mcp`, and sets up audit logging.
|
32
|
+
3. Start the server:
|
33
|
+
- Rails: `rails server` (MCP at `/mcp`)
|
34
|
+
- Standalone: `bundle exec rails-active-mcp-server`
|
35
|
+
- Rack: `rackup mcp.ru -p 3001`
|
36
|
+
|
37
|
+
## Configuration
|
38
|
+
Edit `config/initializers/rails_active_mcp.rb`:
|
39
|
+
- Enable/disable the server
|
40
|
+
- Safety mode (production = strict)
|
41
|
+
- Allowed/blocked models
|
42
|
+
- Enable/disable mutation tools
|
43
|
+
- Audit log location
|
44
|
+
- Environment presets: `production_mode!`, `strict_mode!`, `permissive_mode!`
|
45
|
+
|
46
|
+
## Available Tools
|
47
|
+
- **rails_console_execute**: Execute Ruby code in Rails with safety checks
|
48
|
+
- **rails_model_info**: Inspect model schema and associations
|
49
|
+
- **rails_safe_query**: Run safe, read-only queries on models
|
50
|
+
- **rails_dry_run**: Analyze code for safety without executing
|
51
|
+
|
52
|
+
Each tool is described in the MCP `tools/list` response and can be extended or customized.
|
53
|
+
|
54
|
+
## Security & Safety
|
55
|
+
- **Safety Checker**: Blocks dangerous operations (e.g., mass deletions, system commands, file access)
|
56
|
+
- **Read-Only Mode**: Enforced in production
|
57
|
+
- **Audit Logging**: All executions are logged to `log/rails_active_mcp.log`
|
58
|
+
- **Configurable**: Fine-tune what is allowed per environment
|
59
|
+
|
60
|
+
## Extending the Gem
|
61
|
+
- Add custom tools by calling `RailsActiveMcp.server.register_tool` in an initializer or plugin.
|
62
|
+
- Tools must define a name, description, input schema, and a handler block.
|
63
|
+
|
64
|
+
## Testing & Specification Adherence
|
65
|
+
- All features are covered by RSpec tests in the `spec/` directory.
|
66
|
+
- Follows standard Ruby/Rails conventions for gems, engines, and generators.
|
67
|
+
- MCP protocol compliance is maintained as per [modelcontextprotocol.io](https://modelcontextprotocol.io/introduction).
|
68
|
+
|
69
|
+
## Per-Tool Breakdown
|
70
|
+
|
71
|
+
### 1. rails_console_execute
|
72
|
+
**Purpose:**
|
73
|
+
Execute arbitrary Ruby code in the Rails console context, with safety checks and output capture.
|
74
|
+
|
75
|
+
**Input Parameters:**
|
76
|
+
- `code` (string, required): Ruby code to execute.
|
77
|
+
- `safe_mode` (boolean, optional): Enable safety checks (default: true).
|
78
|
+
- `timeout` (integer, optional): Timeout in seconds (default: 30).
|
79
|
+
- `capture_output` (boolean, optional): Capture console output (default: true).
|
80
|
+
|
81
|
+
**Output:**
|
82
|
+
- On success: Code, result, output (if any), execution time, and notes.
|
83
|
+
- On error: Error message and class.
|
84
|
+
|
85
|
+
**Example Usage:**
|
86
|
+
```json
|
87
|
+
{
|
88
|
+
"method": "tools/call",
|
89
|
+
"params": {
|
90
|
+
"name": "rails_console_execute",
|
91
|
+
"arguments": {
|
92
|
+
"code": "User.count"
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
96
|
+
```
|
97
|
+
|
98
|
+
---
|
99
|
+
|
100
|
+
### 2. rails_model_info
|
101
|
+
**Purpose:**
|
102
|
+
Get detailed information about a Rails model, including schema, associations, and validations.
|
103
|
+
|
104
|
+
**Input Parameters:**
|
105
|
+
- `model` (string, required): Model class name (e.g., "User").
|
106
|
+
- `include_schema` (boolean, optional): Include database schema info (default: true).
|
107
|
+
- `include_associations` (boolean, optional): Include model associations (default: true).
|
108
|
+
- `include_validations` (boolean, optional): Include model validations (default: true).
|
109
|
+
|
110
|
+
**Output:**
|
111
|
+
- Model name, table, primary key, schema (columns, types, null/default), associations, validations.
|
112
|
+
- On error: Error message (e.g., model not found).
|
113
|
+
|
114
|
+
**Example Usage:**
|
115
|
+
```json
|
116
|
+
{
|
117
|
+
"method": "tools/call",
|
118
|
+
"params": {
|
119
|
+
"name": "rails_model_info",
|
120
|
+
"arguments": {
|
121
|
+
"model": "User"
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
```
|
126
|
+
|
127
|
+
---
|
128
|
+
|
129
|
+
### 3. rails_safe_query
|
130
|
+
**Purpose:**
|
131
|
+
Execute safe, read-only database queries on Rails models.
|
132
|
+
|
133
|
+
**Input Parameters:**
|
134
|
+
- `model` (string, required): Model class name (e.g., "User").
|
135
|
+
- `method` (string, required): Query method (e.g., "where", "count").
|
136
|
+
- `args` (array, optional): Arguments for the query method.
|
137
|
+
- `limit` (integer, optional): Limit results (default: 100).
|
138
|
+
|
139
|
+
**Output:**
|
140
|
+
- Query string, count, and result (as inspected Ruby object).
|
141
|
+
- On error: Error message.
|
142
|
+
|
143
|
+
**Example Usage:**
|
144
|
+
```json
|
145
|
+
{
|
146
|
+
"method": "tools/call",
|
147
|
+
"params": {
|
148
|
+
"name": "rails_safe_query",
|
149
|
+
"arguments": {
|
150
|
+
"model": "User",
|
151
|
+
"method": "where",
|
152
|
+
"args": [{"active": true}],
|
153
|
+
"limit": 10
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
157
|
+
```
|
158
|
+
|
159
|
+
---
|
160
|
+
|
161
|
+
### 4. rails_dry_run
|
162
|
+
**Purpose:**
|
163
|
+
Analyze Ruby code for safety and risk without executing it.
|
164
|
+
|
165
|
+
**Input Parameters:**
|
166
|
+
- `code` (string, required): Ruby code to analyze.
|
167
|
+
|
168
|
+
**Output:**
|
169
|
+
- Code, safety status, read-only status, risk level, summary, violations, and recommendations.
|
170
|
+
|
171
|
+
**Example Usage:**
|
172
|
+
```json
|
173
|
+
{
|
174
|
+
"method": "tools/call",
|
175
|
+
"params": {
|
176
|
+
"name": "rails_dry_run",
|
177
|
+
"arguments": {
|
178
|
+
"code": "User.delete_all"
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
```
|
183
|
+
|
184
|
+
---
|
185
|
+
For more details, see the main project README or source code.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'rack'
|
5
|
+
require 'rack/handler/webrick'
|
6
|
+
require_relative '../lib/rails_active_mcp'
|
7
|
+
|
8
|
+
# Parse command line options
|
9
|
+
port = ARGV.include?('--port') ? ARGV[ARGV.index('--port') + 1].to_i : 3001
|
10
|
+
host = ARGV.include?('--host') ? ARGV[ARGV.index('--host') + 1] : 'localhost'
|
11
|
+
|
12
|
+
puts "Starting Rails Active MCP Server on #{host}:#{port}"
|
13
|
+
puts "Press Ctrl+C to stop"
|
14
|
+
|
15
|
+
begin
|
16
|
+
Rack::Handler::WEBrick.run(
|
17
|
+
RailsActiveMcp::McpServer.new,
|
18
|
+
Port: port,
|
19
|
+
Host: host,
|
20
|
+
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN)
|
21
|
+
)
|
22
|
+
rescue Interrupt
|
23
|
+
puts "\nShutting down server..."
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RailsActiveMcp::InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
desc 'Install Rails Active MCP'
|
7
|
+
|
8
|
+
def create_initializer
|
9
|
+
template 'initializer.rb', 'config/initializers/rails_active_mcp.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_mcp_route
|
13
|
+
route "mount RailsActiveMcp::McpServer.new, at: '/mcp' # Rails Active MCP"
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_mcp_config
|
17
|
+
template 'mcp.ru', 'mcp.ru'
|
18
|
+
end
|
19
|
+
|
20
|
+
def show_readme
|
21
|
+
readme 'README.md' if behavior == :invoke
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def readme(path)
|
27
|
+
readme_path = File.join(self.class.source_root, path)
|
28
|
+
if File.exist?(readme_path)
|
29
|
+
say IO.read(readme_path), :green
|
30
|
+
else
|
31
|
+
say "README file not found at #{readme_path}", :yellow
|
32
|
+
end
|
33
|
+
rescue => e
|
34
|
+
say "Error reading README: #{e.message}", :red
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
================================================================================
|
3
|
+
Rails Active MCP has been installed!
|
4
|
+
================================================================================
|
5
|
+
|
6
|
+
Configuration:
|
7
|
+
- Initializer created at: config/initializers/rails_active_mcp.rb
|
8
|
+
- Custom MCP server mounted at: /mcp
|
9
|
+
- MCP server configuration: mcp.ru
|
10
|
+
- Audit log will be created at: log/rails_active_mcp.log
|
11
|
+
|
12
|
+
Next Steps:
|
13
|
+
|
14
|
+
1. Review and customize the configuration in config/initializers/rails_active_mcp.rb
|
15
|
+
|
16
|
+
2. Start the MCP server:
|
17
|
+
Option A: Rails-mounted server
|
18
|
+
$ rails server
|
19
|
+
|
20
|
+
Option B: Standalone server
|
21
|
+
$ bundle exec rails-active-mcp-server
|
22
|
+
|
23
|
+
Option C: Using rackup
|
24
|
+
$ rackup mcp.ru -p 3001
|
25
|
+
|
26
|
+
3. For Warp Terminal integration, add this to your MCP configuration:
|
27
|
+
{
|
28
|
+
"mcpServers": {
|
29
|
+
"rails-console": {
|
30
|
+
"command": "curl",
|
31
|
+
"args": ["-X", "POST", "-H", "Content-Type: application/json", "-d", "@-", "http://localhost:3000/mcp"]
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
4. Test the installation:
|
37
|
+
$ rails console
|
38
|
+
> RailsActiveMcp.safe?("User.count")
|
39
|
+
> RailsActiveMcp.execute("User.count")
|
40
|
+
|
41
|
+
Built-in Tools:
|
42
|
+
- rails_console_execute: Execute Ruby code with safety checks
|
43
|
+
|
44
|
+
Extend with custom tools by modifying the MCP server implementation.
|
45
|
+
|
46
|
+
Security Notes:
|
47
|
+
- Production mode enables strict safety by default
|
48
|
+
- All executions are logged to the audit file
|
49
|
+
- Dangerous operations are blocked in safe mode
|
50
|
+
- Review the safety patterns in the configuration
|
51
|
+
|
52
|
+
Custom MCP Server Benefits:
|
53
|
+
- No external dependencies
|
54
|
+
- Full control over implementation
|
55
|
+
- Simplified deployment
|
56
|
+
- Enhanced security
|
57
|
+
|
58
|
+
For more information: https://github.com/goodpie/rails-active-mcp
|
59
|
+
|
60
|
+
================================================================================
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rails_active_mcp'
|
2
|
+
|
3
|
+
|
4
|
+
RailsActiveMcp.configure do |config|
|
5
|
+
# Enable/disable the MCP server
|
6
|
+
config.enabled = true
|
7
|
+
|
8
|
+
# Safety configuration
|
9
|
+
config.safe_mode = Rails.env.production? # Always safe in production
|
10
|
+
config.default_timeout = 30 # seconds
|
11
|
+
config.max_results = 100
|
12
|
+
|
13
|
+
# Model access control
|
14
|
+
# config.allowed_models = %w[User Post Comment] # Empty means all allowed
|
15
|
+
# config.blocked_models = %w[AdminUser Secret] # Models to never allow access
|
16
|
+
|
17
|
+
# Mutation tools (dangerous operations)
|
18
|
+
config.enable_mutation_tools = !Rails.env.production?
|
19
|
+
|
20
|
+
# Logging and auditing
|
21
|
+
config.log_executions = true
|
22
|
+
config.audit_file = Rails.root.join("log", "rails_active_mcp.log")
|
23
|
+
|
24
|
+
# Environment-specific settings
|
25
|
+
case Rails.env
|
26
|
+
when 'production'
|
27
|
+
config.production_mode! # Very strict settings
|
28
|
+
when 'development'
|
29
|
+
config.permissive_mode! # More relaxed for development
|
30
|
+
when 'test'
|
31
|
+
config.strict_mode! # Safe for testing
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add custom safety patterns
|
35
|
+
# config.add_safety_pattern(/CustomDangerousMethod/, "Custom dangerous operation")
|
36
|
+
|
37
|
+
# Operations that require manual confirmation
|
38
|
+
config.require_confirmation_for = [:delete, :destroy, :update_all, :delete_all]
|
39
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module RailsActiveMcp
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :enabled, :safe_mode, :default_timeout, :max_results,
|
6
|
+
:allowed_models, :blocked_models, :custom_safety_patterns,
|
7
|
+
:log_executions, :audit_file, :enable_mutation_tools,
|
8
|
+
:require_confirmation_for, :execution_environment
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@enabled = true
|
12
|
+
@safe_mode = true
|
13
|
+
@default_timeout = 30
|
14
|
+
@max_results = 100
|
15
|
+
@allowed_models = [] # Empty means all models allowed
|
16
|
+
@blocked_models = []
|
17
|
+
@custom_safety_patterns = []
|
18
|
+
@log_executions = true
|
19
|
+
# Safe Rails.root access
|
20
|
+
@audit_file = rails_root_join("log", "rails_active_mcp.log") if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
21
|
+
@enable_mutation_tools = false
|
22
|
+
@require_confirmation_for = [:delete, :destroy, :update_all, :delete_all]
|
23
|
+
@execution_environment = :current # :current, :sandbox, :readonly_replica
|
24
|
+
end
|
25
|
+
|
26
|
+
# Safety configuration
|
27
|
+
def strict_mode!
|
28
|
+
@safe_mode = true
|
29
|
+
@enable_mutation_tools = false
|
30
|
+
@default_timeout = 15
|
31
|
+
@max_results = 50
|
32
|
+
end
|
33
|
+
|
34
|
+
def permissive_mode!
|
35
|
+
@safe_mode = false
|
36
|
+
@enable_mutation_tools = true
|
37
|
+
@default_timeout = 60
|
38
|
+
@max_results = 1000
|
39
|
+
end
|
40
|
+
|
41
|
+
def production_mode!
|
42
|
+
strict_mode!
|
43
|
+
@execution_environment = :readonly_replica
|
44
|
+
@log_executions = true
|
45
|
+
@require_confirmation_for = [:delete, :destroy, :update, :create, :save]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Model access configuration
|
49
|
+
def allow_models(*models)
|
50
|
+
@allowed_models.concat(models.map(&:to_s))
|
51
|
+
end
|
52
|
+
|
53
|
+
def block_models(*models)
|
54
|
+
@blocked_models.concat(models.map(&:to_s))
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_safety_pattern(pattern, description = nil)
|
58
|
+
@custom_safety_patterns << { pattern: pattern, description: description }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Validation
|
62
|
+
def model_allowed?(model_name)
|
63
|
+
model_str = model_name.to_s
|
64
|
+
|
65
|
+
# Check if specifically blocked
|
66
|
+
return false if @blocked_models.include?(model_str)
|
67
|
+
|
68
|
+
# If allow list is empty, allow all (except blocked)
|
69
|
+
return true if @allowed_models.empty?
|
70
|
+
|
71
|
+
# Check allow list
|
72
|
+
@allowed_models.include?(model_str)
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate!
|
76
|
+
raise ArgumentError, "timeout must be positive" if @default_timeout <= 0
|
77
|
+
raise ArgumentError, "max_results must be positive" if @max_results <= 0
|
78
|
+
|
79
|
+
if defined?(Rails) && @audit_file
|
80
|
+
audit_dir = File.dirname(@audit_file)
|
81
|
+
FileUtils.mkdir_p(audit_dir) unless File.directory?(audit_dir)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def rails_root_join(*args)
|
88
|
+
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
89
|
+
Rails.root.join(*args)
|
90
|
+
else
|
91
|
+
File.join(Dir.pwd, *args)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|