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.
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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'config/environment'
4
+ require 'rails_active_mcp'
5
+
6
+ # Run the Rails Active MCP server
7
+ run RailsActiveMcp::McpServer.new
@@ -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