active_mcp 0.3.10 → 0.4.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: f210104dfa86d207d50b806da95f4fb9b5f7da18d440d8c9d93c37eac6ebccce
4
- data.tar.gz: 94bf8d285dfd0abf889bf097794b2fba7a0f9901aa3d17923df3094ee697d984
3
+ metadata.gz: acd47906160af7e22cbfa3fe974288e7d16639898b026ef6288ab91444e799be
4
+ data.tar.gz: 0bc89e6d457537dbe75450f1ad9ec525ed37b9168df48f08c10adbbcb2aebc5f
5
5
  SHA512:
6
- metadata.gz: 2cd44b67933bc753e3bf3019661a1b3b4cbab44c1889beb9e6a8b59ef8269dae34536600b38d5ebcb8d305f33c9af9072b408f8677c0c2a05985b72f3a18897d
7
- data.tar.gz: f873ac0ab2d283cf8e7279509f311faeb61d662856c00aea4336931007a5d0b8950072282bd6f7b43eaccea4c4a147ec6207251ae610be3c59b5331a5906a03f
6
+ metadata.gz: ea4c71409b27eb65027b33fa0e43ddbaf4e3924768625d28788a2d23e9604099fc14eb6de3c962066a260a96bac8415605066ad05c4cc3ff63f31a33a47fbbc3
7
+ data.tar.gz: 4960bc1dee62aa05245159e0d53cf618583739e63aecbbdaf581db3cbed6133317a2537ec9f1d12313956c5e00facb24a6f1fefa32899d685061671fe529adfc
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,75 @@ 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 < ActiveMcp::Resource
318
+ uri "data://localhost/user"
319
+ mime_type "application/json"
320
+ description "User profile data"
321
+
322
+ def text(auth_info: nil)
323
+ # Authenticate if needed
324
+ user = User.find_by(id: 1)
325
+
326
+ # Return JSON data
327
+ {
328
+ id: user.id,
329
+ name: user.name,
330
+ email: user.email,
331
+ created_at: user.created_at
332
+ }
333
+ end
334
+ end
335
+ ```
336
+
337
+ ### Resource Types
338
+
339
+ Resources can return two types of content:
340
+
341
+ 1. **Text Content** - Use the `text` method to return structured data:
342
+
343
+ ```ruby
344
+ def text(auth_info: nil)
345
+ # Return strings, arrays, hashes, or any JSON-serializable object
346
+ { items: Product.all.map(&:attributes) }
347
+ end
348
+ ```
349
+
350
+ 2. **Binary Content** - Use the `blob` method to return binary files:
351
+
352
+ ```ruby
353
+ class ImageResource < ActiveMcp::Resource
354
+ uri "data://localhost/image"
355
+ mime_type "image/png"
356
+ description "Profile image"
357
+
358
+ def blob(auth_info: nil)
359
+ # Return binary file content
360
+ File.read(Rails.root.join("public", "profile.png"))
361
+ end
362
+ end
363
+ ```
364
+
365
+ Resources can be protected using the same authorization mechanism as tools:
366
+
367
+ ```ruby
368
+ def self.visible?(auth_info)
369
+ return false unless auth_info
370
+ return false unless auth_info[:type] == :bearer
371
+
372
+ # Check if the token belongs to an admin
373
+ User.find_by_token(auth_info[:token])&.admin?
374
+ end
375
+ ```
376
+
293
377
  ## ⚙️ Advanced Configuration
294
378
 
295
379
  ### Custom Controller
@@ -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 = ActiveMcp::Resource.authorized_resources(auth_info)
40
+ @format = :jsonrpc
41
+ render 'active_mcp/resources_list', formats: :json
42
+ when Method::RESOURCES_READ
43
+ @resource = ActiveMcp::ResourceReader.read(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 = ActiveMcp::Resource.authorized_resources(auth_info)
66
+ @format = :json
67
+ render 'active_mcp/resources_list', formats: :json
68
+ when Method::RESOURCES_READ
69
+ @resource = ActiveMcp::ResourceReader.read(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,78 @@
1
+ module ActiveMcp
2
+ class ResourceReader
3
+ def self.read(params:, auth_info:)
4
+ if params[:jsonrpc].present?
5
+ uri = params[:params][:uri]
6
+ else
7
+ uri = params[:uri]
8
+ end
9
+
10
+ unless uri
11
+ return {
12
+ isError: true,
13
+ contents: []
14
+ }
15
+ end
16
+
17
+ resource_class = Resource.registered_resources.find do |r|
18
+ r._uri == uri
19
+ end
20
+
21
+ unless resource_class
22
+ return {
23
+ isError: true,
24
+ contents: []
25
+ }
26
+ end
27
+
28
+ unless resource_class.visible?(auth_info)
29
+ return {
30
+ isError: true,
31
+ contents: []
32
+ }
33
+ end
34
+
35
+ resource = resource_class.new
36
+
37
+ begin
38
+ if content = resource.text(auth_info:)
39
+ return {
40
+ contents: [
41
+ {
42
+ uri:,
43
+ mimeType: resource_class._mime_type,
44
+ text: formatted(content)
45
+ }
46
+ ]
47
+ }
48
+ elsif content = resource.blob(auth_info:)
49
+ return {
50
+ contents: [
51
+ {
52
+ uri:,
53
+ mimeType: resource_class._mime_type,
54
+ blob: Base64.strict_encode64(content)
55
+ }
56
+ ]
57
+ }
58
+ end
59
+ rescue
60
+ return {
61
+ isError: true,
62
+ contents: []
63
+ }
64
+ end
65
+ end
66
+
67
+ def self.formatted(object)
68
+ case object
69
+ when String
70
+ object
71
+ when Hash
72
+ object.to_json
73
+ else
74
+ object.to_s
75
+ end
76
+ end
77
+ end
78
+ end
@@ -110,7 +110,7 @@ module ActiveMcp
110
110
  when String
111
111
  object
112
112
  when Hash
113
- object.to_yaml
113
+ object.to_json
114
114
  else
115
115
  object.to_s
116
116
  end
@@ -1,2 +1,2 @@
1
1
  json.jsonrpc ActiveMcp::JSON_RPC_VERSION
2
- json.result true
2
+ json.method ActiveMcp::Method::CANCELLED
@@ -0,0 +1,10 @@
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 @resources
7
+ end
8
+ else
9
+ json.result @resources
10
+ 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
@@ -5,5 +5,10 @@ if @format == :jsonrpc
5
5
  json.result @tool_result
6
6
  else
7
7
  json.isError @tool_result[:isError] if @tool_result[:isError]
8
- json.content @tool_result[:content]
8
+ json.content do
9
+ json.array!(@tool_result[:content]) do |content|
10
+ json.type content[:type]
11
+ json.text raw content[:text]
12
+ end
13
+ end
9
14
  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
@@ -0,0 +1,64 @@
1
+ require "json-schema"
2
+
3
+ module ActiveMcp
4
+ class Resource
5
+ class << self
6
+ attr_reader :_description, :schema, :_uri, :_mime_type
7
+
8
+ def resource_name
9
+ name ? name.underscore.sub(/_resource$/, "") : ""
10
+ end
11
+
12
+ def uri(value)
13
+ @_uri = value
14
+ end
15
+
16
+ def description(value)
17
+ @_description = value
18
+ end
19
+
20
+ def mime_type(value)
21
+ @_mime_type = value
22
+ end
23
+
24
+ def registered_resources
25
+ @registered_resources ||= []
26
+ end
27
+
28
+ attr_writer :registered_resources
29
+
30
+ def inherited(subclass)
31
+ registered_resources << subclass
32
+ end
33
+
34
+ def visible?(auth_info)
35
+ if respond_to?(:authorized?)
36
+ authorized?(auth_info)
37
+ else
38
+ true
39
+ end
40
+ end
41
+
42
+ def authorized_resources(auth_info = nil)
43
+ registered_resources.select do |tool_class|
44
+ tool_class.visible?(auth_info)
45
+ end.map do |tool_class|
46
+ {
47
+ uri: tool_class._uri,
48
+ name: tool_class.resource_name,
49
+ mimeType: tool_class._mime_type,
50
+ description: tool_class._description
51
+ }
52
+ end
53
+ end
54
+ end
55
+
56
+ def text(auth_info:)
57
+ nil
58
+ end
59
+
60
+ def blob(auth_info:)
61
+ nil
62
+ end
63
+ end
64
+ 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.10"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/active_mcp.rb CHANGED
@@ -4,6 +4,7 @@ require "jbuilder"
4
4
  require_relative "active_mcp/version"
5
5
  require_relative "active_mcp/configuration"
6
6
  require_relative "active_mcp/tool"
7
+ require_relative "active_mcp/resource"
7
8
  require_relative "active_mcp/server"
8
9
 
9
10
  if defined? ::Rails
@@ -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,37 @@
1
+ class <%= class_name %> < ActiveMcp::Resource
2
+ uri "data://localhost/<%= file_name %>"
3
+ mime_type "application/json"
4
+ description "<%= file_name.humanize %>"
5
+
6
+ # Uncomment and modify this method to implement authorization control
7
+ # This controls who can see and use this tool
8
+ # def self.visible?(auth_info)
9
+ # # Example: require authentication
10
+ # # return false unless auth_info
11
+ #
12
+ # # Example: require a specific authentication type
13
+ # # return false unless auth_info[:type] == :bearer
14
+ #
15
+ # # Example: check for admin permissions
16
+ # # admin_tokens = ["admin-token"]
17
+ # # return admin_tokens.include?(auth_info[:token])
18
+ #
19
+ # # Default: allow all access
20
+ # true
21
+ # end
22
+
23
+ def text(auth_info: nil)
24
+ # Authentication information can be accessed via _auth_info parameter
25
+ # auth_info = { type: :bearer, token: "xxx", header: "Bearer xxx" }
26
+ # or { type: :basic, token: "base64encoded", header: "Basic base64encoded" }
27
+
28
+ # Implement tool logic here
29
+
30
+ # Return a string, hash, or any JSON-serializable object
31
+ { foo: "bar" }
32
+ end
33
+
34
+ # def blob(auth_info: nil)
35
+ # File.read("/path/to/file")
36
+ # end
37
+ 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.10
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moeki Kawakami
@@ -69,27 +69,34 @@ files:
69
69
  - Rakefile
70
70
  - app/controllers/active_mcp/base_controller.rb
71
71
  - app/controllers/concerns/active_mcp/request_handler.rb
72
+ - app/models/active_mcp/resource_reader.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
80
83
  - lib/active_mcp.rb
81
84
  - lib/active_mcp/configuration.rb
82
85
  - lib/active_mcp/engine.rb
86
+ - lib/active_mcp/resource.rb
83
87
  - lib/active_mcp/server.rb
84
88
  - lib/active_mcp/server/error_codes.rb
85
- - lib/active_mcp/server/methods.rb
89
+ - lib/active_mcp/server/method.rb
86
90
  - lib/active_mcp/server/protocol_handler.rb
91
+ - lib/active_mcp/server/resource_manager.rb
87
92
  - lib/active_mcp/server/stdio_connection.rb
88
93
  - lib/active_mcp/server/tool_manager.rb
89
94
  - lib/active_mcp/tool.rb
90
95
  - lib/active_mcp/version.rb
91
96
  - lib/generators/active_mcp/install/install_generator.rb
92
97
  - lib/generators/active_mcp/install/templates/initializer.rb
98
+ - lib/generators/active_mcp/resource/resource_generator.rb
99
+ - lib/generators/active_mcp/resource/templates/resource.rb.erb
93
100
  - lib/generators/active_mcp/tool/templates/tool.rb.erb
94
101
  - lib/generators/active_mcp/tool/tool_generator.rb
95
102
  homepage: https://github.com/moekiorg/active_mcp
File without changes