active_mcp 0.3.4 โ†’ 0.3.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c67705b1452f86930b120080e883a10cdb4d231bf2a8a533621adb55b3099c6
4
- data.tar.gz: 62d30c1eba1c08c25b6c0e8a74337e3c735a3d14fe85c8f81a19505a0db248cd
3
+ metadata.gz: 36a188cb2b45d872122cddac68b6d7ce052c432d11e36dab471b0b050b86cecc
4
+ data.tar.gz: 1704a7bb34a996d3168a419bc425147eccc941d1f9c4b11f14abc38c3632e41a
5
5
  SHA512:
6
- metadata.gz: ce288f544ec46e5a5c44da9651c344fa985e17b730dbd463230cb86143f1bb8ddd2992c64219ee3eacb9dc3264820ae1b92d4c751938dd01dcac5ac3b2a6d2e0
7
- data.tar.gz: 9709e2ce17bbe2f86fd3e15a3ecee1748a7708b69b5ecf79843cd94a342fabb1009563d2651d0d8377f0335c663e9d9e26b3017587d33d1fdb3ab7e580577aca
6
+ metadata.gz: '0380f2010e79b71a2beb552448129eebe646895fb2af2c35490d093b93714e45453da3403f6c1a85b3bd3ea8df84ab4fff6d6d21ba38dc7f94c7b14340d789bf'
7
+ data.tar.gz: 758c49617295edeba4231a8b80cf64758739b14e722b23a3904695c29d0b4ff1e3a003d195e589c69a6b933019bf4f2a27ed847dc831ad4fcdf2bdf7ef65b7b8
data/README.md CHANGED
@@ -1,8 +1,54 @@
1
- # Active MCP
2
-
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
-
5
- ## Installation
1
+ # Active MCP ๐Ÿ”Œ
2
+
3
+ <div align="center">
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/active_mcp.svg)](https://badge.fury.io/rb/active_mcp)
6
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+ [![Rails](https://img.shields.io/badge/Rails-%3E%3D%206.0.0-red.svg)](https://rubyonrails.org/)
8
+
9
+ A Ruby on Rails engine for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) - connect your Rails apps to AI tools with minimal effort.
10
+ </div>
11
+
12
+ ## ๐Ÿ“– Table of Contents
13
+
14
+ - [Active MCP ๐Ÿ”Œ](#active-mcp-)
15
+ - [๐Ÿ“– Table of Contents](#-table-of-contents)
16
+ - [โœจ Features](#-features)
17
+ - [๐Ÿ“ฆ Installation](#-installation)
18
+ - [๐Ÿš€ Setup](#-setup)
19
+ - [Using the Install Generator (Recommended)](#using-the-install-generator-recommended)
20
+ - [Manual Setup](#manual-setup)
21
+ - [๐Ÿ”Œ MCP Connection Methods](#-mcp-connection-methods)
22
+ - [1. Direct HTTP Connection](#1-direct-http-connection)
23
+ - [2. Standalone MCP Server](#2-standalone-mcp-server)
24
+ - [๐Ÿ›  Rails Generators](#-rails-generators)
25
+ - [Install Generator](#install-generator)
26
+ - [Tool Generator](#tool-generator)
27
+ - [๐Ÿงฐ Creating MCP Tools](#-creating-mcp-tools)
28
+ - [๐Ÿ“‹ Input Schema](#-input-schema)
29
+ - [๐Ÿ” Authorization \& Authentication](#-authorization--authentication)
30
+ - [Authorization for Tools](#authorization-for-tools)
31
+ - [Authentication Options](#authentication-options)
32
+ - [1. Server Configuration](#1-server-configuration)
33
+ - [2. Token Verification in Tools](#2-token-verification-in-tools)
34
+ - [โš™๏ธ Advanced Configuration](#๏ธ-advanced-configuration)
35
+ - [Custom Controller](#custom-controller)
36
+ - [๐Ÿ’ก Best Practices](#-best-practices)
37
+ - [1. Create Specific Tool Classes](#1-create-specific-tool-classes)
38
+ - [2. Validate and Sanitize Inputs](#2-validate-and-sanitize-inputs)
39
+ - [3. Return Structured Responses](#3-return-structured-responses)
40
+ - [๐Ÿงช Development](#-development)
41
+ - [๐Ÿ‘ฅ Contributing](#-contributing)
42
+ - [๐Ÿ“„ License](#-license)
43
+
44
+ ## โœจ Features
45
+
46
+ - **Simple Integration**: Easily expose Rails functionality as MCP tools
47
+ - **Powerful Generators**: Quickly scaffold MCP tools with Rails generators
48
+ - **Authentication Support**: Built-in authentication and authorization capabilities
49
+ - **Flexible Configuration**: Multiple deployment and connection options
50
+
51
+ ## ๐Ÿ“ฆ Installation
6
52
 
7
53
  Add this line to your application's Gemfile:
8
54
 
@@ -22,7 +68,7 @@ Or install it yourself as:
22
68
  $ gem install active_mcp
23
69
  ```
24
70
 
25
- ## Setup
71
+ ## ๐Ÿš€ Setup
26
72
 
27
73
  ### Using the Install Generator (Recommended)
28
74
 
@@ -36,6 +82,8 @@ This generator will:
36
82
 
37
83
  1. Create a configuration initializer at `config/initializers/active_mcp.rb`
38
84
  2. Mount the ActiveMcp engine in your routes
85
+ 3. Create an MCP server script at `script/mcp_server.rb`
86
+ 4. Show instructions for next steps
39
87
 
40
88
  After running the generator, follow the displayed instructions to create and configure your MCP tools.
41
89
 
@@ -57,50 +105,58 @@ end
57
105
 
58
106
  ```ruby
59
107
  class CreateNoteTool < ActiveMcp::Tool
60
- description "Create Note!!"
108
+ description "Create Note"
61
109
 
62
- argument :title, :string
63
- argument :content, :string
110
+ argument :title, :string, required: true
111
+ argument :content, :string, required: true
64
112
 
65
113
  def call(title:, content:)
66
- Note.create(title:, content:)
114
+ note = Note.create(title: title, content: content)
67
115
 
68
- "Created!"
116
+ "Created note with ID: #{note.id}"
69
117
  end
70
118
  end
71
119
  ```
72
120
 
73
- #### with streamable HTTP
121
+ ## ๐Ÿ”Œ MCP Connection Methods
122
+
123
+ Active MCP supports two connection methods:
74
124
 
75
- Set MCP destination to `https:your-app.example.com/mcp`
125
+ ### 1. Direct HTTP Connection
126
+
127
+ Set your MCP client to connect directly to your Rails application:
128
+
129
+ ```
130
+ https://your-app.example.com/mcp
131
+ ```
76
132
 
77
- #### with independent MCP Server
133
+ ### 2. Standalone MCP Server
78
134
 
79
- Start the MCP server:
135
+ Start a dedicated MCP server that communicates with your Rails app:
80
136
 
81
137
  ```ruby
82
- # server.rb
138
+ # script/mcp_server.rb
83
139
  server = ActiveMcp::Server.new(
84
- name: "ActiveMcp DEMO",
140
+ name: "My App MCP Server",
85
141
  uri: 'https://your-app.example.com/mcp'
86
142
  )
87
143
  server.start
88
144
  ```
89
145
 
90
- Set up MCP Client
146
+ Then configure your MCP client:
91
147
 
92
148
  ```json
93
149
  {
94
150
  "mcpServers": {
95
- "active-mcp-demo": {
151
+ "my-rails-app": {
96
152
  "command": "/path/to/ruby",
97
- "args": ["/path/to/server.rb"]
153
+ "args": ["/path/to/script/mcp_server.rb"]
98
154
  }
99
155
  }
100
156
  }
101
157
  ```
102
158
 
103
- ## Rails Generators
159
+ ## ๐Ÿ›  Rails Generators
104
160
 
105
161
  Active MCP provides generators to help you quickly set up and extend your MCP integration:
106
162
 
@@ -112,39 +168,46 @@ Initialize Active MCP in your Rails application:
112
168
  $ rails generate active_mcp:install
113
169
  ```
114
170
 
115
- This sets up all necessary configuration files and mounts the MCP engine in your routes.
116
-
117
171
  ### Tool Generator
118
172
 
119
173
  Create new MCP tools quickly:
120
174
 
121
175
  ```bash
122
- # Generate a new MCP tool
123
176
  $ rails generate active_mcp:tool search_users
124
177
  ```
125
178
 
126
- This creates a new tool file at `app/tools/search_users_tool.rb` with the following starter code:
179
+ This creates a new tool file at `app/tools/search_users_tool.rb` with ready-to-customize starter code.
180
+
181
+ ## ๐Ÿงฐ Creating MCP Tools
182
+
183
+ MCP tools are Ruby classes that inherit from `ActiveMcp::Tool` and define an interface for AI to interact with your application:
127
184
 
128
185
  ```ruby
129
186
  class SearchUsersTool < ActiveMcp::Tool
130
- description 'Search users'
187
+ description 'Search users by criteria'
131
188
 
132
- argument :param1, :string, required: true, description: 'First parameter description'
133
- argument :param2, :string, required: false, description: 'Second parameter description'
134
- # Add more parameters as needed
189
+ argument :email, :string, required: false, description: 'Email to search for'
190
+ argument :name, :string, required: false, description: 'Name to search for'
191
+ argument :limit, :integer, required: false, description: 'Maximum number of records to return'
135
192
 
136
- def call(param1:, param2: nil, auth_info: nil, **args)
137
- # auth_info = { type: :bearer, token: 'xxx', header: 'Bearer xxx' }
193
+ def call(email: nil, name: nil, limit: 10)
194
+ criteria = {}
195
+ criteria[:email] = email if email.present?
196
+ criteria[:name] = name if name.present?
138
197
 
139
- # Implement your tool logic here
140
- "Tool executed successfully with #{param1}"
198
+ users = User.where(criteria).limit(limit)
199
+
200
+ {
201
+ type: "text",
202
+ content: users.to_json(only: [:id, :name, :email, :created_at])
203
+ }
141
204
  end
142
205
  end
143
206
  ```
144
207
 
145
- You can then customize the generated tool to fit your needs.
208
+ ## ๐Ÿ“‹ Input Schema
146
209
 
147
- ## Input Schema
210
+ Define arguments for your tools using the `argument` method:
148
211
 
149
212
  ```ruby
150
213
  argument :name, :string, required: true, description: 'User name'
@@ -153,45 +216,35 @@ argument :addresses, :array, required: false, description: 'User addresses'
153
216
  argument :preferences, :object, required: false, description: 'User preferences'
154
217
  ```
155
218
 
156
- Supported types include:
157
-
158
- - `:string`
159
- - `:integer`
160
- - `:number` (float/decimal)
161
- - `:boolean`
162
- - `:array`
163
- - `:object` (hash/dictionary)
164
- - `:null`
165
-
166
- ## Using with MCP Clients
219
+ Supported types:
167
220
 
168
- Any MCP-compatible client can connect to your server. The most common way is to provide the MCP server URL:
169
-
170
- ```
171
- http://your-app.example.com/mcp
172
- ```
221
+ | Type | Description |
222
+ | --- | --- |
223
+ | `:string` | Text values |
224
+ | `:integer` | Whole numbers |
225
+ | `:number` | Decimal numbers (float/decimal) |
226
+ | `:boolean` | True/false values |
227
+ | `:array` | Lists of values |
228
+ | `:object` | Hash/dictionary structures |
229
+ | `:null` | Null values |
173
230
 
174
- Clients will discover the available tools and their input schemas automatically through the MCP protocol.
175
-
176
- ## Authorization & Authentication
177
-
178
- ActiveMcp supports both authentication (verifying who a user is) and authorization (controlling what resources they can access).
231
+ ## ๐Ÿ” Authorization & Authentication
179
232
 
180
233
  ### Authorization for Tools
181
234
 
182
- You can control which tools are visible and accessible to different users by overriding the `visible?` class method:
235
+ Control access to tools by overriding the `visible?` class method:
183
236
 
184
237
  ```ruby
185
238
  class AdminOnlyTool < ActiveMcp::Tool
186
- description "This tool is only accessible by admins"
239
+ description "Admin-only tool"
187
240
 
188
- argument :command, :string, required: true, description: "Admin command to execute"
241
+ argument :command, :string, required: true, description: "Admin command"
189
242
 
190
- # Define authorization logic - only admin tokens can access this tool
243
+ # Only allow admins to access this tool
191
244
  def self.visible?(auth_info)
192
245
  return false unless auth_info
193
246
  return false unless auth_info[:type] == :bearer
194
-
247
+
195
248
  # Check if the token belongs to an admin
196
249
  auth_info[:token] == "admin-token" || User.find_by_token(auth_info[:token])&.admin?
197
250
  end
@@ -202,125 +255,56 @@ class AdminOnlyTool < ActiveMcp::Tool
202
255
  end
203
256
  ```
204
257
 
205
- When a user makes a request to the MCP server:
206
-
207
- 1. Only tools that return `true` from their `authorized?` method will be included in the tools list
208
- 2. Users can only call tools that they're authorized to use
209
- 3. Unauthorized access attempts will return a 403 Forbidden response
210
-
211
- This makes it easy to create role-based access control for your MCP tools.
258
+ ### Authentication Options
212
259
 
213
- ### Authentication Flow
214
-
215
- ActiveMcp supports receiving authentication credentials from MCP clients and forwarding them to your Rails application. There are two ways to handle authentication:
216
-
217
- ### 1. Using Server Configuration
218
-
219
- When creating your MCP server, you can pass authentication options that will be included in every request:
260
+ #### 1. Server Configuration
220
261
 
221
262
  ```ruby
222
263
  server = ActiveMcp::Server.new(
223
- name: "ActiveMcp DEMO",
264
+ name: "My Secure MCP Server",
224
265
  uri: 'http://localhost:3000/mcp',
225
266
  auth: {
226
- type: :bearer, # or :basic
227
- token: ENV[:ACCESS_TOKEN]
267
+ type: :bearer,
268
+ token: ENV['MCP_AUTH_TOKEN']
228
269
  }
229
270
  )
230
271
  server.start
231
272
  ```
232
273
 
233
- ### 2. Custom Controller with Auth Handling
234
-
235
- For more advanced authentication, create a custom controller that handles the authentication flow:
274
+ #### 2. Token Verification in Tools
236
275
 
237
276
  ```ruby
238
- class CustomController < ActiveMcpController
239
- before_action :authenticate
240
-
241
- private
242
-
243
- def authenticate
244
- # Extract auth from MCP request
245
- auth_header = request.headers['Authorization']
246
-
247
- if auth_header.present?
248
- # Process the auth header (Bearer token, etc.)
249
- token = auth_header.split(' ').last
250
-
251
- # Validate the token against your auth system
252
- user = User.find_by_token(token)
253
-
254
- unless user
255
- render_error(-32600, "Authentication failed")
256
- return false
257
- end
258
-
259
- # Set current user for tool access
260
- Current.user = user
261
- else
262
- render_error(-32600, "Authentication required")
263
- return false
264
- end
277
+ def call(resource_id:, auth_info: nil, **args)
278
+ # Check if authentication is provided
279
+ unless auth_info.present?
280
+ raise "Authentication required"
265
281
  end
266
- end
267
- ```
268
-
269
- ### 3. Using Auth in Tools
270
-
271
- Authentication information is automatically passed to your tools through the `auth_info` parameter:
272
-
273
- ```ruby
274
- class SecuredDataTool < ActiveMcp::Tool
275
- description 'Access secured data'
276
-
277
- argument :resource_id, :string, required: true, description: 'ID of the resource to access'
278
-
279
- def call(resource_id:, auth_info: nil, **args)
280
- # Check if auth info exists
281
- unless auth_info.present?
282
- raise "Authentication required to access this resource"
283
- end
284
-
285
- # Extract token from auth info
286
- token = auth_info[:token]
287
-
288
- # Validate token and get user
289
- user = User.authenticate_with_token(token)
290
-
291
- unless user
292
- raise "Invalid authentication token"
293
- end
294
-
295
- # Check if user has access to the resource
296
- resource = Resource.find(resource_id)
297
-
298
- if resource.user_id != user.id
299
- raise "Access denied to this resource"
300
- end
301
282
 
302
- # Return the secured data
303
- {
304
- type: "text",
305
- content: resource.to_json
306
- }
283
+ # Verify the token
284
+ user = User.authenticate_with_token(auth_info[:token])
285
+
286
+ unless user
287
+ raise "Invalid authentication token"
307
288
  end
289
+
290
+ # Proceed with authenticated operation
291
+ # ...
308
292
  end
309
293
  ```
310
294
 
311
- ## Advanced Configuration
295
+ ## โš™๏ธ Advanced Configuration
312
296
 
313
297
  ### Custom Controller
314
298
 
315
- If you need to customize the MCP controller behavior, you can create your own controller that inherits from `ActiveMcpController`:
299
+ Create a custom controller for advanced needs:
316
300
 
317
301
  ```ruby
318
- class CustomController < ActiveContexController
319
- # Add custom behavior, authentication, etc.
302
+ class CustomMcpController < ActiveMcp::BaseController
303
+ # Custom MCP handling logic
320
304
  end
321
305
  ```
322
306
 
323
- And update your routes:
307
+ Update routes:
324
308
 
325
309
  ```ruby
326
310
  Rails.application.routes.draw do
@@ -328,72 +312,69 @@ Rails.application.routes.draw do
328
312
  end
329
313
  ```
330
314
 
331
- ## Best Practices
315
+ ## ๐Ÿ’ก Best Practices
332
316
 
333
- ### Create a Tool for Each Model
317
+ ### 1. Create Specific Tool Classes
334
318
 
335
- For security reasons, it's recommended to create specific tools for each model rather than generic tools that dynamically determine the model class. This approach:
336
-
337
- 1. Increases security by avoiding dynamic class loading
338
- 2. Makes your tools more explicit and easier to understand
339
- 3. Provides better validation and error handling specific to each model
340
-
341
- For example, instead of creating a generic search tool, create specific search tools for each model:
319
+ Create dedicated tool classes for each model or operation instead of generic tools:
342
320
 
343
321
  ```ruby
344
- # Good: Specific tool for searching users
322
+ # โœ… GOOD: Specific tool for a single purpose
345
323
  class SearchUsersTool < ActiveMcp::Tool
346
- description 'Search users by criteria'
324
+ # ...specific implementation
325
+ end
347
326
 
348
- argument :email, :string, required: false, description: 'Email to search for'
349
- argument :name, :string, required: false, description: 'Name to search for'
350
- argument :limit, :integer, required: false, description: 'Maximum number of records to return'
327
+ # โŒ BAD: Generic tool that dynamically loads models
328
+ class GenericSearchTool < ActiveMcp::Tool
329
+ # Avoid this pattern - security and maintainability issues
330
+ end
331
+ ```
351
332
 
352
- def call(email: nil, name: nil, limit: 10)
353
- criteria = {}
354
- criteria[:email] = email if email.present?
355
- criteria[:name] = name if name.present?
333
+ ### 2. Validate and Sanitize Inputs
356
334
 
357
- users = User.where(criteria).limit(limit)
335
+ Always validate and sanitize inputs in your tool implementations:
358
336
 
359
- {
360
- type: "text",
361
- content: users.to_json(only: [:id, :name, :email, :created_at])
362
- }
337
+ ```ruby
338
+ def call(user_id:, **args)
339
+ # Validate input
340
+ unless user_id.is_a?(Integer) || user_id.to_s.match?(/^\d+$/)
341
+ return { error: "Invalid user ID format" }
363
342
  end
343
+
344
+ # Proceed with validated data
345
+ user = User.find_by(id: user_id)
346
+ # ...
364
347
  end
348
+ ```
365
349
 
366
- # Good: Specific tool for searching posts
367
- class SearchPostsTool < ActiveMcp::Tool
368
- description 'Search posts by criteria'
369
-
370
- argument :title, :string, required: false, description: 'Title to search for'
371
- argument :author_id, :integer, required: false, description: 'Author ID to filter by'
372
- argument :limit, :integer, required: false, description: 'Maximum number of records to return'
373
-
374
- def call(title: nil, author_id: nil, limit: 10)
375
- criteria = {}
376
- criteria[:title] = title if title.present?
377
- criteria[:author_id] = author_id if author_id.present?
350
+ ### 3. Return Structured Responses
378
351
 
379
- posts = Post.where(criteria).limit(limit)
352
+ Return structured responses that are easy for AI to parse:
380
353
 
381
- {
382
- type: "text",
383
- content: posts.to_json(only: [:id, :title, :author_id, :created_at])
354
+ ```ruby
355
+ def call(query:, **args)
356
+ results = User.search(query)
357
+
358
+ {
359
+ type: "text",
360
+ content: results.to_json(only: [:id, :name, :email]),
361
+ metadata: {
362
+ count: results.size,
363
+ query: query
384
364
  }
385
- end
365
+ }
386
366
  end
387
367
  ```
388
368
 
389
- ## Development
369
+ ## ๐Ÿงช Development
390
370
 
391
371
  After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
392
372
 
393
- ## Contributing
373
+ ## ๐Ÿ‘ฅ Contributing
394
374
 
395
- Bug reports and pull requests are welcome on GitHub at https://github.com/kawakamimoeki/active_mcp. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/kawakamimoeki/active_mcp/blob/main/CODE_OF_CONDUCT.md).
375
+ Bug reports and pull requests are welcome on GitHub at https://github.com/moekiorg/active_mcp. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/moekiorg/active_mcp/blob/main/CODE_OF_CONDUCT.md).
396
376
 
397
- ## License
377
+ ## ๐Ÿ“„ License
398
378
 
399
379
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
380
+
@@ -10,12 +10,5 @@ module ActiveMcp
10
10
  end
11
11
  end
12
12
  end
13
-
14
- initializer "active_mcp.configure_jbuilder" do |app|
15
- if Rails.env.development?
16
- Jbuilder.key_format camelize: :lower
17
- Jbuilder.prettify if Jbuilder.respond_to?(:prettify)
18
- end
19
- end
20
13
  end
21
14
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveMcp
2
- VERSION = "0.3.4"
2
+ VERSION = "0.3.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moeki Kawakami