active_mcp 0.3.11 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67338ce686da0dee13981737b87274009ff6e333e0cf06274469b03f6b066c11
4
- data.tar.gz: 86f3916c8df7848adea802a736f117f37f9a3c2a92f1aca811984b2782562a12
3
+ metadata.gz: 1891356bb19d256dcbd11f7d441fb9c5f668fc9cc0b115ea17eec44aef99ee54
4
+ data.tar.gz: 8031f7e13bfd5a837cf84ff3ca069bd5c56b46901e7e04c861d6c15eabcf2c94
5
5
  SHA512:
6
- metadata.gz: 5a58ff5874317b91aa79f84e373e57b04a0f11378ebe4299a9ce9408f930051038c5b86a1faa3c2e8e0b4a42463850d5e662112e48fae4876812b2c6e8aedafe
7
- data.tar.gz: c64a85603d69a0e15c1cf8e0a42209a7f424e7f95bd0f064fcb785cf9e5b2d70c2ab0b1bb8fd36d8aa0af5dd2367ff767c3f431fbada308a23714f181c98bd6e
6
+ metadata.gz: 20438f69b5a08fa089187a1eb11c0f0e8ccc5525809439e45da36105411b7c62187a981f0a73deda52fdb292ede1ffc429aaa26d0a21f2a5a369b9601bd2306d
7
+ data.tar.gz: cf9c750474af01cc72c1a8480c568c36a29d271b7c7a9f5a2bf5ecdec8cc42e1fc0fe70493ae6e9a82d3f431445aca068a881fc89c71c6181f976384c8b0e2ca
data/README.md CHANGED
@@ -25,6 +25,7 @@ A Ruby on Rails engine for the [Model Context Protocol (MCP)](https://modelconte
25
25
  - [🛠 Rails Generators](#-rails-generators)
26
26
  - [Install Generator](#install-generator)
27
27
  - [Tool Generator](#tool-generator)
28
+ - [Resource Generator](#resource-generator)
28
29
  - [🧰 Creating MCP Tools](#-creating-mcp-tools)
29
30
  - [📋 Input Schema](#-input-schema)
30
31
  - [🔐 Authorization \& Authentication](#-authorization--authentication)
@@ -32,6 +33,9 @@ A Ruby on Rails engine for the [Model Context Protocol (MCP)](https://modelconte
32
33
  - [Authentication Options](#authentication-options)
33
34
  - [1. Server Configuration](#1-server-configuration)
34
35
  - [2. Token Verification in Tools](#2-token-verification-in-tools)
36
+ - [📦 MCP Resources](#-mcp-resources)
37
+ - [Creating Resources](#creating-resources)
38
+ - [Resource Types](#resource-types)
35
39
  - [⚙️ Advanced Configuration](#️-advanced-configuration)
36
40
  - [Custom Controller](#custom-controller)
37
41
  - [💡 Best Practices](#-best-practices)
@@ -45,7 +49,8 @@ A Ruby on Rails engine for the [Model Context Protocol (MCP)](https://modelconte
45
49
  ## ✨ Features
46
50
 
47
51
  - **Simple Integration**: Easily expose Rails functionality as MCP tools
48
- - **Powerful Generators**: Quickly scaffold MCP tools with Rails generators
52
+ - **Resource Support**: Share files and data with AI assistants through MCP resources
53
+ - **Powerful Generators**: Quickly scaffold MCP tools and resources with Rails generators
49
54
  - **Authentication Support**: Built-in authentication and authorization capabilities
50
55
  - **Flexible Configuration**: Multiple deployment and connection options
51
56
 
@@ -179,6 +184,16 @@ $ rails generate active_mcp:tool search_users
179
184
 
180
185
  This creates a new tool file at `app/tools/search_users_tool.rb` with ready-to-customize starter code.
181
186
 
187
+ ### Resource Generator
188
+
189
+ Generate new MCP resources to share data with AI:
190
+
191
+ ```bash
192
+ $ rails generate active_mcp:resource profile_image
193
+ ```
194
+
195
+ This creates a new resource file at `app/resources/profile_image_resource.rb` that you can customize to provide various types of content to AI assistants.
196
+
182
197
  ## 🧰 Creating MCP Tools
183
198
 
184
199
  MCP tools are Ruby classes that inherit from `ActiveMcp::Tool` and define an interface for AI to interact with your application:
@@ -290,6 +305,101 @@ def call(resource_id:, auth_info: nil, **args)
290
305
  end
291
306
  ```
292
307
 
308
+ ## 📦 MCP Resources
309
+
310
+ MCP Resources allow you to share data and files with AI assistants. Resources have a URI, MIME type, and can return either text or binary data.
311
+
312
+ ### Creating Resources
313
+
314
+ Resources are Ruby classes that inherit from `ActiveMcp::Resource`:
315
+
316
+ ```ruby
317
+ class UserResource
318
+ def initialize(id:, auth_info: nil)
319
+ @user = User.find(id)
320
+ @auth_info = auth_info
321
+ end
322
+
323
+ def name
324
+ @user.name
325
+ end
326
+
327
+ def uri
328
+ "data://localhost/users/#{@user.id}"
329
+ end
330
+
331
+ def mime_type
332
+ "application/json"
333
+ end
334
+
335
+ def description
336
+ @user.profile
337
+ end
338
+
339
+ def text
340
+ # Return JSON data
341
+ {
342
+ id: @user.id,
343
+ name: @user.name,
344
+ email: @user.email,
345
+ created_at: @user.created_at
346
+ }
347
+ end
348
+ end
349
+ ```
350
+
351
+ ### Resource Types
352
+
353
+ Resources can return two types of content:
354
+
355
+ 1. **Text Content** - Use the `text` method to return structured data:
356
+
357
+ ```ruby
358
+ def text
359
+ # Return strings, arrays, hashes, or any JSON-serializable object
360
+ { items: Product.all.map(&:attributes) }
361
+ end
362
+ ```
363
+
364
+ 2. **Binary Content** - Use the `blob` method to return binary files:
365
+
366
+ ```ruby
367
+ class ImageResource
368
+ def name
369
+ "image"
370
+ end
371
+
372
+ def uri
373
+ "data://localhost/image"
374
+ end
375
+
376
+ def mime_type
377
+ "image/png"
378
+ end
379
+
380
+ def description
381
+ "Profile image"
382
+ end
383
+
384
+ def blob
385
+ # Return binary file content
386
+ File.read(Rails.root.join("public", "profile.png"))
387
+ end
388
+ end
389
+ ```
390
+
391
+ Resources can be protected using the same authorization mechanism as tools:
392
+
393
+ ```ruby
394
+ def visible?
395
+ return false unless auth_info
396
+ return false unless auth_info[:type] == :bearer
397
+
398
+ # Check if the token belongs to an admin
399
+ User.find_by_token(auth_info[:token])&.admin?
400
+ end
401
+ ```
402
+
293
403
  ## ⚙️ Advanced Configuration
294
404
 
295
405
  ### Custom Controller
@@ -2,6 +2,7 @@
2
2
 
3
3
  module ActiveMcp
4
4
  class BaseController < ActionController::Base
5
- include RequestHandler
5
+ include RequestHandlable
6
+ include ResourceReadable
6
7
  end
7
8
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveMcp
4
- module RequestHandler
4
+ module RequestHandlable
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
@@ -35,6 +35,14 @@ module ActiveMcp
35
35
  render 'active_mcp/initialized', formats: :json
36
36
  when Method::CANCELLED
37
37
  render 'active_mcp/cancelled', formats: :json
38
+ when Method::RESOURCES_LIST
39
+ @resources = resources_list
40
+ @format = :jsonrpc
41
+ render 'active_mcp/resources_list', formats: :json
42
+ when Method::RESOURCES_READ
43
+ @resource = read_resource(params:, auth_info:)
44
+ @format = :jsonrpc
45
+ render 'active_mcp/resources_read', formats: :json
38
46
  when Method::TOOLS_LIST
39
47
  @tools = ActiveMcp::Tool.authorized_tools(auth_info)
40
48
  @format = :jsonrpc
@@ -53,6 +61,14 @@ module ActiveMcp
53
61
  @auth_info = auth_info
54
62
 
55
63
  case params[:method]
64
+ when Method::RESOURCES_LIST
65
+ @resources = resources_list
66
+ @format = :json
67
+ render 'active_mcp/resources_list', formats: :json
68
+ when Method::RESOURCES_READ
69
+ @resource = read_resource(params:, auth_info:)
70
+ @format = :json
71
+ render 'active_mcp/resources_read', formats: :json
56
72
  when Method::TOOLS_LIST
57
73
  @tools = ActiveMcp::Tool.authorized_tools(auth_info)
58
74
  @format = :json
@@ -0,0 +1,84 @@
1
+ module ActiveMcp
2
+ module ResourceReadable
3
+ extend ActiveSupport::Concern
4
+
5
+ private
6
+
7
+ def resources_list
8
+ []
9
+ end
10
+
11
+ def read_resource(params:, auth_info:)
12
+ if params[:jsonrpc].present?
13
+ uri = params[:params][:uri]
14
+ else
15
+ uri = params[:uri]
16
+ end
17
+
18
+ unless uri
19
+ return {
20
+ isError: true,
21
+ contents: []
22
+ }
23
+ end
24
+
25
+ resource = resources_list.find do |r|
26
+ r.uri == uri
27
+ end
28
+
29
+ unless resource
30
+ return {
31
+ isError: true,
32
+ contents: []
33
+ }
34
+ end
35
+
36
+ if resource.respond_to?(:visible?) && !resource.visible?
37
+ return {
38
+ isError: true,
39
+ contents: []
40
+ }
41
+ end
42
+
43
+ begin
44
+ if content = resource.text
45
+ return {
46
+ contents: [
47
+ {
48
+ uri:,
49
+ mimeType: resource.mime_type,
50
+ text: formatted(content)
51
+ }
52
+ ]
53
+ }
54
+ elsif content = resource.blob
55
+ return {
56
+ contents: [
57
+ {
58
+ uri:,
59
+ mimeType: resource.mime_type,
60
+ blob: Base64.strict_encode64(content)
61
+ }
62
+ ]
63
+ }
64
+ end
65
+ rescue
66
+ return {
67
+ isError: true,
68
+ contents: []
69
+ }
70
+ end
71
+ end
72
+
73
+ def formatted(object)
74
+ case object
75
+ when String
76
+ object
77
+ when Hash
78
+ object.to_json
79
+ else
80
+ object.to_s
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,24 @@
1
+ json.jsonrpc ActiveMcp::JSON_RPC_VERSION if @format == :jsonrpc
2
+ json.id @id if @format == :jsonrpc && @id.present?
3
+
4
+ if @format == :jsonrpc
5
+ json.result do
6
+ json.resources do
7
+ json.array!(@resources) do |resource|
8
+ json.name resource.name
9
+ json.uri resource.uri
10
+ json.mimeType resource.mime_type
11
+ json.description resource.description
12
+ end
13
+ end
14
+ end
15
+ else
16
+ json.result do
17
+ json.array!(@resources) do |resource|
18
+ json.name resource.name
19
+ json.uri resource.uri
20
+ json.mimeType resource.mime_type
21
+ json.description resource.description
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ json.jsonrpc ActiveMcp::JSON_RPC_VERSION if @format == :jsonrpc
2
+ json.id @id if @format == :jsonrpc && @id.present?
3
+
4
+ if @format == :jsonrpc
5
+ json.result @resource
6
+ else
7
+ json.isError @resource[:isError] if @resource[:isError]
8
+ json.contents do
9
+ json.array!(@resource[:contents]) do |content|
10
+ json.uri content[:uri]
11
+ json.mimeType raw content[:mimeType]
12
+ json.text raw content[:text] if content[:text]
13
+ json.blob content[:blob] if content[:blob]
14
+ end
15
+ end
16
+ end
@@ -7,4 +7,4 @@ if @format == :jsonrpc
7
7
  end
8
8
  else
9
9
  json.result @tools
10
- end
10
+ end
@@ -10,5 +10,14 @@ module ActiveMcp
10
10
  end
11
11
  end
12
12
  end
13
+
14
+ initializer "active_mcp.eager_load_resources" do |app|
15
+ tools_path = Rails.root.join("app", "resources")
16
+ if Dir.exist?(tools_path)
17
+ Dir[tools_path.join("*.rb")].sort.each do |file|
18
+ require_dependency file
19
+ end
20
+ end
21
+ end
13
22
  end
14
23
  end
@@ -48,12 +48,12 @@ module ActiveMcp
48
48
  handle_initialized(request)
49
49
  when Method::PING
50
50
  handle_ping(request)
51
+ when Method::RESOURCES_LIST
52
+ handle_list_resources(request)
51
53
  when Method::TOOLS_LIST
52
54
  handle_list_tools(request)
53
55
  when Method::TOOLS_CALL
54
56
  handle_use_tool(request)
55
- when Method::RESOURCES_LIST
56
- handle_list_resources(request)
57
57
  when Method::RESOURCES_READ
58
58
  handle_read_resource(request)
59
59
  else
@@ -112,6 +112,10 @@ module ActiveMcp
112
112
  success_response(request[:id], {})
113
113
  end
114
114
 
115
+ def handle_list_resources(request)
116
+ success_response(request[:id], {resources: @server.resource_manager.resources})
117
+ end
118
+
115
119
  def handle_list_tools(request)
116
120
  success_response(request[:id], {tools: @server.tool_manager.tools})
117
121
  end
@@ -130,19 +134,16 @@ module ActiveMcp
130
134
  end
131
135
  end
132
136
 
133
- def handle_list_resources(request)
134
- success_response(
135
- request[:id],
136
- {
137
- resources: [],
138
- nextCursor: "0"
139
- }
140
- )
141
- end
142
-
143
137
  def handle_read_resource(request)
144
138
  uri = request.dig(:params, :uri)
145
- error_response(request[:id], ErrorCode::INVALID_REQUEST, "Resource not found", {uri: uri})
139
+ begin
140
+ result = @server.resource_manager.read_resource(uri)
141
+
142
+ success_response(request[:id], result)
143
+ rescue => e
144
+ log_error("Error reading resource #{uri}", e)
145
+ error_response(request[:id], ErrorCode::INTERNAL_ERROR, "An error occurred while reading the resource")
146
+ end
146
147
  end
147
148
 
148
149
  def success_response(id, result)
@@ -0,0 +1,150 @@
1
+ require "json"
2
+
3
+ module ActiveMcp
4
+ class Server
5
+ class ResourceManager
6
+ attr_reader :resources
7
+
8
+ def initialize(uri: nil, auth: nil)
9
+ @resources = {}
10
+ @base_uri = uri
11
+
12
+ if auth
13
+ @auth_header = "#{auth[:type] == :bearer ? "Bearer" : "Basic"} #{auth[:token]}"
14
+ end
15
+ end
16
+
17
+ def load_registered_resources
18
+ fetch_resources
19
+ end
20
+
21
+ def read_resource(uri)
22
+ require "net/http"
23
+
24
+ unless @base_uri.is_a?(URI) || @base_uri.is_a?(String)
25
+ log_error("Invalid URI type", StandardError.new("URI must be a String or URI object"))
26
+ return {
27
+ isError: true,
28
+ content: [{type: "text", text: "Invalid URI configuration"}]
29
+ }
30
+ end
31
+
32
+ begin
33
+ base_uri = URI.parse(@base_uri.to_s)
34
+
35
+ unless base_uri.scheme =~ /\Ahttps?\z/ && !base_uri.host.nil?
36
+ log_error("Invalid URI", StandardError.new("URI must have a valid scheme and host"))
37
+ return {
38
+ isError: true,
39
+ content: [{type: "text", text: "Invalid URI configuration"}]
40
+ }
41
+ end
42
+
43
+ if defined?(Rails) && Rails.env.production? && base_uri.scheme != "https"
44
+ return {
45
+ isError: true,
46
+ content: [{type: "text", text: "HTTPS is required in production environment"}]
47
+ }
48
+ end
49
+ rescue URI::InvalidURIError => e
50
+ log_error("Invalid URI format", e)
51
+ return {
52
+ isError: true,
53
+ content: [{type: "text", text: "Invalid URI format"}]
54
+ }
55
+ end
56
+
57
+ request = Net::HTTP::Post.new(base_uri)
58
+ request.body = JSON.generate({
59
+ method: Method::RESOURCES_READ,
60
+ uri:,
61
+ })
62
+ request["Content-Type"] = "application/json"
63
+ request["Authorization"] = @auth_header
64
+
65
+ begin
66
+ response = Net::HTTP.start(base_uri.hostname, base_uri.port) do |http|
67
+ http.request(request)
68
+ end
69
+
70
+ if response.code == "200"
71
+ JSON.parse(response.body, symbolize_names: true)
72
+ else
73
+ $stderr.puts(response.body)
74
+ {
75
+ isError: true,
76
+ contents: []
77
+ }
78
+ end
79
+ rescue => e
80
+ log_error("Error calling tool", e)
81
+ {
82
+ isError: true,
83
+ contents: []
84
+ }
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def fetch_resources
91
+ return unless @base_uri
92
+
93
+ require "net/http"
94
+
95
+ unless @base_uri.is_a?(URI) || @base_uri.is_a?(String)
96
+ log_error("Invalid URI type", StandardError.new("URI must be a String or URI object"))
97
+ return
98
+ end
99
+
100
+ begin
101
+ uri = URI.parse(@base_uri.to_s)
102
+
103
+ unless uri.scheme =~ /\Ahttps?\z/ && !uri.host.nil?
104
+ log_error("Invalid URI", StandardError.new("URI must have a valid scheme and host"))
105
+ return
106
+ end
107
+
108
+ if defined?(Rails) && Rails.env.production? && uri.scheme != "https"
109
+ log_error("HTTPS is required in production environment", StandardError.new("Non-HTTPS URI in production"))
110
+ return
111
+ end
112
+ rescue URI::InvalidURIError => e
113
+ log_error("Invalid URI format", e)
114
+ return
115
+ end
116
+
117
+ request = Net::HTTP::Post.new(uri)
118
+ request.body = JSON.generate({
119
+ method: "resources/list",
120
+ arguments: "{}"
121
+ })
122
+ request["Content-Type"] = "application/json"
123
+ request["Authorization"] = @auth_header
124
+
125
+ begin
126
+ response = Net::HTTP.start(uri.hostname, uri.port) do |http|
127
+ http.request(request)
128
+ end
129
+
130
+ result = JSON.parse(response.body, symbolize_names: true)
131
+ @resources = result[:result]
132
+ rescue => e
133
+ log_error("Error fetching resources", e)
134
+ @resources = []
135
+ end
136
+ end
137
+
138
+ def log_error(message, error)
139
+ error_details = "#{message}: #{error.message}\n"
140
+ error_details += error.backtrace.join("\n") if error.backtrace
141
+
142
+ if defined?(Rails)
143
+ Rails.logger.error(error_details)
144
+ else
145
+ $stderr.puts(error_details)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,14 +1,15 @@
1
1
  require "json"
2
2
  require "English"
3
- require_relative "server/methods"
3
+ require_relative "server/method"
4
4
  require_relative "server/error_codes"
5
5
  require_relative "server/stdio_connection"
6
+ require_relative "server/resource_manager"
6
7
  require_relative "server/tool_manager"
7
8
  require_relative "server/protocol_handler"
8
9
 
9
10
  module ActiveMcp
10
11
  class Server
11
- attr_reader :name, :version, :uri, :tool_manager, :protocol_handler
12
+ attr_reader :name, :version, :uri, :tool_manager, :protocol_handler, :resource_manager
12
13
 
13
14
  def initialize(
14
15
  version: ActiveMcp::VERSION,
@@ -19,9 +20,11 @@ module ActiveMcp
19
20
  @name = name
20
21
  @version = version
21
22
  @uri = uri
23
+ @resource_manager = ResourceManager.new(uri:, auth:)
22
24
  @tool_manager = ToolManager.new(uri: uri, auth:)
23
25
  @protocol_handler = ProtocolHandler.new(self)
24
26
  @tool_manager.load_registered_tools
27
+ @resource_manager.load_registered_resources
25
28
  end
26
29
 
27
30
  def start
@@ -1,3 +1,3 @@
1
1
  module ActiveMcp
2
- VERSION = "0.3.11"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -0,0 +1,17 @@
1
+ module ActiveMcp
2
+ module Generators
3
+ class ResourceGenerator < Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def create_resource_file
7
+ template "resource.rb.erb", File.join("app/resources", "#{file_name}_resource.rb")
8
+ end
9
+
10
+ private
11
+
12
+ def class_name
13
+ "#{file_name.camelize}Resource"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ class <%= class_name %>
2
+ def initialize(auth_info:)
3
+ @auth_info = auth_info
4
+
5
+ # Authentication information can be accessed via @auth_info parameter
6
+ # @auth_info = { type: :bearer, token: "xxx", header: "Bearer xxx" }
7
+ # or { type: :basic, token: "base64encoded", header: "Basic base64encoded" }
8
+ end
9
+
10
+ def name
11
+ "<%= file_name %>"
12
+ end
13
+
14
+ def uri
15
+ "data://localhost/<%= file_name %>"
16
+ end
17
+
18
+ def mime_type
19
+ "application/json"
20
+ end
21
+
22
+ def description
23
+ "<%= file_name.humanize %>"
24
+ end
25
+
26
+ # Uncomment and modify this method to implement authorization control
27
+ # This controls who can see and use this tool
28
+ # def visible?
29
+ # # Example: require authentication
30
+ # # return false unless @auth_info
31
+ #
32
+ # # Example: require a specific authentication type
33
+ # # return false unless @auth_info[:type] == :bearer
34
+ #
35
+ # # Example: check for admin permissions
36
+ # # admin_tokens = ["admin-token"]
37
+ # # return admin_tokens.include?(@auth_info[:token])
38
+ #
39
+ # # Default: allow all access
40
+ # true
41
+ # end
42
+
43
+ def text
44
+ # Return a string, hash, or any JSON-serializable object
45
+ { foo: "bar" }
46
+ end
47
+
48
+ # def blob
49
+ # File.read("/path/to/file")
50
+ # end
51
+ end
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.3.11
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moeki Kawakami
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-06 00:00:00.000000000 Z
10
+ date: 2025-04-07 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -68,12 +68,15 @@ files:
68
68
  - README.md
69
69
  - Rakefile
70
70
  - app/controllers/active_mcp/base_controller.rb
71
- - app/controllers/concerns/active_mcp/request_handler.rb
71
+ - app/controllers/concerns/active_mcp/request_handlable.rb
72
+ - app/controllers/concerns/active_mcp/resource_readable.rb
72
73
  - app/models/active_mcp/tool_executor.rb
73
74
  - app/views/active_mcp/cancelled.json.jbuilder
74
75
  - app/views/active_mcp/initialize.json.jbuilder
75
76
  - app/views/active_mcp/initialized.json.jbuilder
76
77
  - app/views/active_mcp/no_method.json.jbuilder
78
+ - app/views/active_mcp/resources_list.json.jbuilder
79
+ - app/views/active_mcp/resources_read.json.jbuilder
77
80
  - app/views/active_mcp/tools_call.json.jbuilder
78
81
  - app/views/active_mcp/tools_list.json.jbuilder
79
82
  - config/routes.rb
@@ -82,14 +85,17 @@ files:
82
85
  - lib/active_mcp/engine.rb
83
86
  - lib/active_mcp/server.rb
84
87
  - lib/active_mcp/server/error_codes.rb
85
- - lib/active_mcp/server/methods.rb
88
+ - lib/active_mcp/server/method.rb
86
89
  - lib/active_mcp/server/protocol_handler.rb
90
+ - lib/active_mcp/server/resource_manager.rb
87
91
  - lib/active_mcp/server/stdio_connection.rb
88
92
  - lib/active_mcp/server/tool_manager.rb
89
93
  - lib/active_mcp/tool.rb
90
94
  - lib/active_mcp/version.rb
91
95
  - lib/generators/active_mcp/install/install_generator.rb
92
96
  - lib/generators/active_mcp/install/templates/initializer.rb
97
+ - lib/generators/active_mcp/resource/resource_generator.rb
98
+ - lib/generators/active_mcp/resource/templates/resource.rb.erb
93
99
  - lib/generators/active_mcp/tool/templates/tool.rb.erb
94
100
  - lib/generators/active_mcp/tool/tool_generator.rb
95
101
  homepage: https://github.com/moekiorg/active_mcp
File without changes