active_mcp 0.2.1 → 0.3.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 +4 -4
- data/README.md +45 -7
- data/app/controllers/active_mcp/base_controller.rb +90 -20
- data/lib/active_mcp/config.rb +22 -0
- data/lib/active_mcp/server/methods.rb +1 -0
- data/lib/active_mcp/server/protocol_handler.rb +1 -1
- data/lib/active_mcp/server/tool_manager.rb +1 -1
- data/lib/active_mcp/version.rb +1 -1
- data/lib/active_mcp.rb +1 -0
- data/lib/generators/active_mcp/install/install_generator.rb +17 -0
- data/lib/generators/active_mcp/install/templates/initializer.rb +5 -0
- data/lib/generators/active_mcp/install/templates/mcp_server.rb +31 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 051dd51506aaadb0c6bf460f1f001049b5d70dc950a060a663d2256050224de7
|
4
|
+
data.tar.gz: e70d63d1618f882dd9d459de896a5ba086e29cbef9acbdf897f800a76266850e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b5c6aaa00d5f9138d2186f2ea4c988f621f5f231674f4c01eeda64418187e0502999b1385c9417824bd99b888a9252f2833c5f5f42026e8943ce07d8a8952a4
|
7
|
+
data.tar.gz: 31d7ea0b9f9cd7b6385a30ced3a9caf14d2105f366d614c1ae8fc5c227f617c547f1f86c63084e4816265cb838ed8e089a4997e9cd1d3e757f8304cd197717a7
|
data/README.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
A Ruby on Rails engine that provides [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) capabilities to Rails applications. This gem allows you to easily create and expose MCP-compatible tools from your Rails application.
|
4
4
|
|
5
|
-

|
6
|
-
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
Add this line to your application's Gemfile:
|
@@ -26,6 +24,25 @@ $ gem install active_mcp
|
|
26
24
|
|
27
25
|
## Setup
|
28
26
|
|
27
|
+
### Using the Install Generator (Recommended)
|
28
|
+
|
29
|
+
The easiest way to set up Active MCP in your Rails application is to use the install generator:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
$ rails generate active_mcp:install
|
33
|
+
```
|
34
|
+
|
35
|
+
This generator will:
|
36
|
+
|
37
|
+
1. Create a configuration initializer at `config/initializers/active_mcp.rb`
|
38
|
+
2. Mount the ActiveMcp engine in your routes
|
39
|
+
|
40
|
+
After running the generator, follow the displayed instructions to create and configure your MCP tools.
|
41
|
+
|
42
|
+
### Manual Setup
|
43
|
+
|
44
|
+
If you prefer to set up manually:
|
45
|
+
|
29
46
|
1. Mount the ActiveMcp engine in your `config/routes.rb`:
|
30
47
|
|
31
48
|
```ruby
|
@@ -53,7 +70,13 @@ class CreateNoteTool < ActiveMcp::Tool
|
|
53
70
|
end
|
54
71
|
```
|
55
72
|
|
56
|
-
|
73
|
+
#### with streamable HTTP
|
74
|
+
|
75
|
+
Set MCP destination as `https:your-app.example.com/mcp`
|
76
|
+
|
77
|
+
#### with independent MCP Server
|
78
|
+
|
79
|
+
Start the MCP server:
|
57
80
|
|
58
81
|
```ruby
|
59
82
|
# server.rb
|
@@ -64,7 +87,7 @@ server = ActiveMcp::Server.new(
|
|
64
87
|
server.start
|
65
88
|
```
|
66
89
|
|
67
|
-
|
90
|
+
Set up MCP Client
|
68
91
|
|
69
92
|
```json
|
70
93
|
{
|
@@ -79,14 +102,28 @@ server.start
|
|
79
102
|
|
80
103
|
## Rails Generators
|
81
104
|
|
82
|
-
MCP
|
105
|
+
Active MCP provides generators to help you quickly set up and extend your MCP integration:
|
106
|
+
|
107
|
+
### Install Generator
|
108
|
+
|
109
|
+
Initialize Active MCP in your Rails application:
|
110
|
+
|
111
|
+
```bash
|
112
|
+
$ rails generate active_mcp:install
|
113
|
+
```
|
114
|
+
|
115
|
+
This sets up all necessary configuration files and mounts the MCP engine in your routes.
|
116
|
+
|
117
|
+
### Tool Generator
|
118
|
+
|
119
|
+
Create new MCP tools quickly:
|
83
120
|
|
84
121
|
```bash
|
85
122
|
# Generate a new MCP tool
|
86
123
|
$ rails generate active_mcp:tool search_users
|
87
124
|
```
|
88
125
|
|
89
|
-
This creates a new tool file at `app/
|
126
|
+
This creates a new tool file at `app/tools/search_users_tool.rb` with the following starter code:
|
90
127
|
|
91
128
|
```ruby
|
92
129
|
class SearchUsersTool < ActiveMcp::Tool
|
@@ -154,7 +191,7 @@ class AdminOnlyTool < ActiveMcp::Tool
|
|
154
191
|
def self.authorized?(auth_info)
|
155
192
|
return false unless auth_info
|
156
193
|
return false unless auth_info[:type] == :bearer
|
157
|
-
|
194
|
+
|
158
195
|
# Check if the token belongs to an admin
|
159
196
|
auth_info[:token] == "admin-token" || User.find_by_token(auth_info[:token])&.admin?
|
160
197
|
end
|
@@ -166,6 +203,7 @@ end
|
|
166
203
|
```
|
167
204
|
|
168
205
|
When a user makes a request to the MCP server:
|
206
|
+
|
169
207
|
1. Only tools that return `true` from their `authorized?` method will be included in the tools list
|
170
208
|
2. Users can only call tools that they're authorized to use
|
171
209
|
3. Unauthorized access attempts will return a 403 Forbidden response
|
@@ -8,10 +8,30 @@ module ActiveMcp
|
|
8
8
|
|
9
9
|
def index
|
10
10
|
case params[:method]
|
11
|
+
when Method::INITIALIZE
|
12
|
+
render_initialize
|
13
|
+
when Method::INITIALIZED
|
14
|
+
render json: {
|
15
|
+
jsonrpc: "2.0",
|
16
|
+
method: "notifications/initialized"
|
17
|
+
}
|
18
|
+
when Method::CANCELLED
|
19
|
+
render json: {
|
20
|
+
jsonrpc: "2.0",
|
21
|
+
method: "notifications/cancelled"
|
22
|
+
}
|
11
23
|
when Method::TOOLS_LIST
|
12
|
-
|
24
|
+
if params[:jsonrpc]
|
25
|
+
render_tools_list_as_jsonrpc
|
26
|
+
else
|
27
|
+
render_tools_list
|
28
|
+
end
|
13
29
|
when Method::TOOLS_CALL
|
14
|
-
|
30
|
+
if params[:jsonrpc]
|
31
|
+
call_tool_as_jsonrpc
|
32
|
+
else
|
33
|
+
render json: call_tool(params)
|
34
|
+
end
|
15
35
|
else
|
16
36
|
render json: {error: "Method not found: #{params[:method]}"}, status: 404
|
17
37
|
end
|
@@ -19,6 +39,29 @@ module ActiveMcp
|
|
19
39
|
|
20
40
|
private
|
21
41
|
|
42
|
+
def render_tools_list_as_jsonrpc
|
43
|
+
render json: {
|
44
|
+
jsonrpc: "2.0",
|
45
|
+
id: params[:id],
|
46
|
+
result: {tools:}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def call_tool_as_jsonrpc
|
51
|
+
render json: {
|
52
|
+
jsonrpc: "2.0",
|
53
|
+
id: params[:id],
|
54
|
+
result: {
|
55
|
+
content: [
|
56
|
+
{
|
57
|
+
type: "text",
|
58
|
+
text: call_tool(params[:params])[:result]
|
59
|
+
}
|
60
|
+
]
|
61
|
+
}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
22
65
|
def process_authentication
|
23
66
|
auth_header = request.headers["Authorization"]
|
24
67
|
if auth_header.present?
|
@@ -36,10 +79,38 @@ module ActiveMcp
|
|
36
79
|
end
|
37
80
|
end
|
38
81
|
|
82
|
+
def render_initialize
|
83
|
+
render json: {
|
84
|
+
jsonrpc: "2.0",
|
85
|
+
id: params[:id],
|
86
|
+
result: {
|
87
|
+
protocolVersion: "2024-11-05",
|
88
|
+
capabilities: {
|
89
|
+
logging: {},
|
90
|
+
capabilities: {
|
91
|
+
resources: {
|
92
|
+
subscribe: false,
|
93
|
+
listChanged: false
|
94
|
+
},
|
95
|
+
tools: {
|
96
|
+
listChanged: false
|
97
|
+
}
|
98
|
+
},
|
99
|
+
},
|
100
|
+
serverInfo: {
|
101
|
+
name: ActiveMcp::Configuration.config.server_name,
|
102
|
+
version: ActiveMcp::Configuration.config.server_version
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
39
108
|
def render_tools_list
|
40
|
-
|
41
|
-
|
42
|
-
|
109
|
+
render json: {result: tools}
|
110
|
+
end
|
111
|
+
|
112
|
+
def tools
|
113
|
+
Tool.registered_tools.select do |tool_class|
|
43
114
|
tool_class.authorized?(@auth_info)
|
44
115
|
end.map do |tool_class|
|
45
116
|
{
|
@@ -48,17 +119,13 @@ module ActiveMcp
|
|
48
119
|
inputSchema: tool_class.schema
|
49
120
|
}
|
50
121
|
end
|
51
|
-
|
52
|
-
render json: {result: tools}
|
53
122
|
end
|
54
123
|
|
55
124
|
def call_tool(params)
|
56
125
|
tool_name = params[:name]
|
57
|
-
arguments = JSON.parse(params[:arguments] || "{}")
|
58
126
|
|
59
127
|
unless tool_name
|
60
|
-
|
61
|
-
return
|
128
|
+
return {error: "Invalid params: missing tool name"}
|
62
129
|
end
|
63
130
|
|
64
131
|
tool_class = Tool.registered_tools.find do |tc|
|
@@ -66,31 +133,34 @@ module ActiveMcp
|
|
66
133
|
end
|
67
134
|
|
68
135
|
unless tool_class
|
69
|
-
|
70
|
-
return
|
136
|
+
return {error: "Tool not found: #{tool_name}"}
|
71
137
|
end
|
72
|
-
|
73
|
-
# 認可チェック
|
138
|
+
|
74
139
|
unless tool_class.authorized?(@auth_info)
|
75
|
-
|
76
|
-
|
140
|
+
return {error: "Unauthorized: Access to tool '#{tool_name}' denied"}
|
141
|
+
end
|
142
|
+
|
143
|
+
if params[:arguments].is_a?(Hash)
|
144
|
+
arguments = params[:arguments].symbolize_keys
|
145
|
+
else
|
146
|
+
arguments = params[:arguments].permit!.to_hash.symbolize_keys.transform_values { _1.match(/^\d+$/) ? _1.to_i : _1 }
|
77
147
|
end
|
78
148
|
|
79
149
|
tool = tool_class.new
|
80
150
|
validation_result = tool.validate_arguments(arguments)
|
81
151
|
|
82
152
|
if validation_result.is_a?(Hash) && validation_result[:error]
|
83
|
-
|
84
|
-
return
|
153
|
+
return {result: validation_result[:error]}
|
85
154
|
end
|
86
155
|
|
87
156
|
begin
|
88
157
|
arguments[:auth_info] = @auth_info if @auth_info.present?
|
89
158
|
|
90
159
|
result = tool.call(**arguments.symbolize_keys)
|
91
|
-
|
160
|
+
|
161
|
+
return {result: result}
|
92
162
|
rescue => e
|
93
|
-
|
163
|
+
return {error: "Error: #{e.message}"}
|
94
164
|
end
|
95
165
|
end
|
96
166
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveMcp
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :server_name, :server_version
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@server_name = "MCP Server"
|
9
|
+
@server_version = "1.0.0"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def configure
|
15
|
+
yield config
|
16
|
+
end
|
17
|
+
|
18
|
+
def config
|
19
|
+
@config ||= Configuration.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -173,7 +173,7 @@ module ActiveMcp
|
|
173
173
|
if defined?(Rails)
|
174
174
|
Rails.logger.error(error_details)
|
175
175
|
else
|
176
|
-
#
|
176
|
+
# Fresallback to standard error output if Rails is not available
|
177
177
|
$stderr.puts(error_details)
|
178
178
|
end
|
179
179
|
end
|
data/lib/active_mcp/version.rb
CHANGED
data/lib/active_mcp.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveMcp
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
desc "Creates an Active MCP initializer and mounts the engine in your routes"
|
7
|
+
|
8
|
+
def create_initializer_file
|
9
|
+
template "initializer.rb", "config/initializers/active_mcp.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_routes
|
13
|
+
route "mount ActiveMcp::Engine, at: '/mcp'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# MCP Server script for Active MCP
|
3
|
+
require 'active_mcp'
|
4
|
+
|
5
|
+
# Load Rails application
|
6
|
+
ENV['RAILS_ENV'] ||= 'development'
|
7
|
+
require File.expand_path('../config/environment', __dir__)
|
8
|
+
|
9
|
+
# Initialize MCP server
|
10
|
+
server = ActiveMcp::Server.new(
|
11
|
+
name: "<%= Rails.application.class.module_parent_name %> MCP Server",
|
12
|
+
uri: '<%= Rails.application.config.action_controller.default_url_options&.fetch(:host, 'http://localhost:3000') %>/mcp'
|
13
|
+
)
|
14
|
+
|
15
|
+
# Optional authentication
|
16
|
+
<% if ActiveMcp.config.auth_enabled %>
|
17
|
+
server.auth = {
|
18
|
+
type: :bearer,
|
19
|
+
token: ENV['MCP_AUTH_TOKEN'] || '<%= ActiveMcp.config.auth_token %>'
|
20
|
+
}
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
# Start the server
|
24
|
+
puts "Starting MCP server for <%= Rails.application.class.module_parent_name %>..."
|
25
|
+
puts "Connect to this server in MCP clients using the following configuration:"
|
26
|
+
puts
|
27
|
+
puts " Command: #{File.expand_path(__FILE__)}"
|
28
|
+
puts " Args: []"
|
29
|
+
puts
|
30
|
+
puts "Press Ctrl+C to stop the server"
|
31
|
+
server.start
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_mcp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Your Name
|
8
|
-
bindir:
|
8
|
+
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-05 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- app/controllers/active_mcp/base_controller.rb
|
57
57
|
- config/routes.rb
|
58
58
|
- lib/active_mcp.rb
|
59
|
+
- lib/active_mcp/config.rb
|
59
60
|
- lib/active_mcp/engine.rb
|
60
61
|
- lib/active_mcp/server.rb
|
61
62
|
- lib/active_mcp/server/error_codes.rb
|
@@ -65,6 +66,9 @@ files:
|
|
65
66
|
- lib/active_mcp/server/tool_manager.rb
|
66
67
|
- lib/active_mcp/tool.rb
|
67
68
|
- lib/active_mcp/version.rb
|
69
|
+
- lib/generators/active_mcp/install/install_generator.rb
|
70
|
+
- lib/generators/active_mcp/install/templates/initializer.rb
|
71
|
+
- lib/generators/active_mcp/install/templates/mcp_server.rb
|
68
72
|
- lib/generators/active_mcp/tool/templates/tool.rb.erb
|
69
73
|
- lib/generators/active_mcp/tool/tool_generator.rb
|
70
74
|
homepage: https://github.com/moekiorg/active_mcp
|