active_mcp 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +44 -18
- data/app/controllers/active_mcp/base_controller.rb +3 -1
- data/app/controllers/concerns/active_mcp/{request_handler.rb → request_handlable.rb} +7 -7
- data/app/{models/active_mcp/resource_reader.rb → controllers/concerns/active_mcp/resource_readable.rb} +22 -16
- data/app/{models/active_mcp/tool_executor.rb → controllers/concerns/active_mcp/tool_executable.rb} +7 -3
- data/app/views/active_mcp/resources_list.json.jbuilder +16 -2
- data/app/views/active_mcp/tools_list.json.jbuilder +1 -1
- data/lib/active_mcp/version.rb +1 -1
- data/lib/active_mcp.rb +0 -1
- data/lib/generators/active_mcp/resource/templates/resource.rb.erb +30 -16
- metadata +5 -6
- data/lib/active_mcp/resource.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6663bc950db47a5719e93db048f5601acb9dd58b87421fe0eb94e2c78cec2333
|
4
|
+
data.tar.gz: ffacf7506cc82ad8fc680dd57f3f74ee50875e8bb7053ff460dc28e59c232cfa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6631c864d9b2fbf566f5202eb31c16a4cb0154a0bcac33b23165c365adab03a01b7ee54da8996dd87660c78046457d2baa6c23f1e1e4dd5a57ea6c06601d84ca
|
7
|
+
data.tar.gz: b3f85f31d012218cafba456a4ebe9b50de9261686aec9b40d1f48a783e74bf6dba9506e113fb0d4372f8e5f82b6000911ec40016151063759d1b2865b68c7abe
|
data/README.md
CHANGED
@@ -314,21 +314,35 @@ MCP Resources allow you to share data and files with AI assistants. Resources ha
|
|
314
314
|
Resources are Ruby classes that inherit from `ActiveMcp::Resource`:
|
315
315
|
|
316
316
|
```ruby
|
317
|
-
class UserResource
|
318
|
-
|
319
|
-
|
320
|
-
|
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
|
321
334
|
|
322
|
-
def
|
323
|
-
|
324
|
-
|
335
|
+
def description
|
336
|
+
@user.profile
|
337
|
+
end
|
325
338
|
|
339
|
+
def text
|
326
340
|
# Return JSON data
|
327
341
|
{
|
328
|
-
id: user.id,
|
329
|
-
name: user.name,
|
330
|
-
email: user.email,
|
331
|
-
created_at: user.created_at
|
342
|
+
id: @user.id,
|
343
|
+
name: @user.name,
|
344
|
+
email: @user.email,
|
345
|
+
created_at: @user.created_at
|
332
346
|
}
|
333
347
|
end
|
334
348
|
end
|
@@ -341,7 +355,7 @@ Resources can return two types of content:
|
|
341
355
|
1. **Text Content** - Use the `text` method to return structured data:
|
342
356
|
|
343
357
|
```ruby
|
344
|
-
def text
|
358
|
+
def text
|
345
359
|
# Return strings, arrays, hashes, or any JSON-serializable object
|
346
360
|
{ items: Product.all.map(&:attributes) }
|
347
361
|
end
|
@@ -350,12 +364,24 @@ end
|
|
350
364
|
2. **Binary Content** - Use the `blob` method to return binary files:
|
351
365
|
|
352
366
|
```ruby
|
353
|
-
class ImageResource
|
354
|
-
|
355
|
-
|
356
|
-
|
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
|
357
383
|
|
358
|
-
def blob
|
384
|
+
def blob
|
359
385
|
# Return binary file content
|
360
386
|
File.read(Rails.root.join("public", "profile.png"))
|
361
387
|
end
|
@@ -365,7 +391,7 @@ end
|
|
365
391
|
Resources can be protected using the same authorization mechanism as tools:
|
366
392
|
|
367
393
|
```ruby
|
368
|
-
def
|
394
|
+
def visible?
|
369
395
|
return false unless auth_info
|
370
396
|
return false unless auth_info[:type] == :bearer
|
371
397
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveMcp
|
4
|
-
module
|
4
|
+
module RequestHandlable
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
@@ -36,11 +36,11 @@ module ActiveMcp
|
|
36
36
|
when Method::CANCELLED
|
37
37
|
render 'active_mcp/cancelled', formats: :json
|
38
38
|
when Method::RESOURCES_LIST
|
39
|
-
@resources =
|
39
|
+
@resources = resources_list
|
40
40
|
@format = :jsonrpc
|
41
41
|
render 'active_mcp/resources_list', formats: :json
|
42
42
|
when Method::RESOURCES_READ
|
43
|
-
@resource =
|
43
|
+
@resource = read_resource(params:, auth_info:)
|
44
44
|
@format = :jsonrpc
|
45
45
|
render 'active_mcp/resources_read', formats: :json
|
46
46
|
when Method::TOOLS_LIST
|
@@ -48,7 +48,7 @@ module ActiveMcp
|
|
48
48
|
@format = :jsonrpc
|
49
49
|
render 'active_mcp/tools_list', formats: :json
|
50
50
|
when Method::TOOLS_CALL
|
51
|
-
@tool_result =
|
51
|
+
@tool_result = execute_tool(params: params, auth_info: auth_info)
|
52
52
|
@format = :jsonrpc
|
53
53
|
render 'active_mcp/tools_call', formats: :json
|
54
54
|
else
|
@@ -62,11 +62,11 @@ module ActiveMcp
|
|
62
62
|
|
63
63
|
case params[:method]
|
64
64
|
when Method::RESOURCES_LIST
|
65
|
-
@resources =
|
65
|
+
@resources = resources_list
|
66
66
|
@format = :json
|
67
67
|
render 'active_mcp/resources_list', formats: :json
|
68
68
|
when Method::RESOURCES_READ
|
69
|
-
@resource =
|
69
|
+
@resource = read_resource(params:, auth_info:)
|
70
70
|
@format = :json
|
71
71
|
render 'active_mcp/resources_read', formats: :json
|
72
72
|
when Method::TOOLS_LIST
|
@@ -74,7 +74,7 @@ module ActiveMcp
|
|
74
74
|
@format = :json
|
75
75
|
render 'active_mcp/tools_list', formats: :json
|
76
76
|
when Method::TOOLS_CALL
|
77
|
-
@tool_result =
|
77
|
+
@tool_result = execute_tool(params: params, auth_info: auth_info)
|
78
78
|
@format = :json
|
79
79
|
render 'active_mcp/tools_call', formats: :json
|
80
80
|
else
|
@@ -1,12 +1,20 @@
|
|
1
1
|
module ActiveMcp
|
2
|
-
|
3
|
-
|
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:)
|
4
12
|
if params[:jsonrpc].present?
|
5
13
|
uri = params[:params][:uri]
|
6
14
|
else
|
7
15
|
uri = params[:uri]
|
8
16
|
end
|
9
|
-
|
17
|
+
|
10
18
|
unless uri
|
11
19
|
return {
|
12
20
|
isError: true,
|
@@ -14,43 +22,41 @@ module ActiveMcp
|
|
14
22
|
}
|
15
23
|
end
|
16
24
|
|
17
|
-
|
18
|
-
r.
|
25
|
+
resource = resources_list.find do |r|
|
26
|
+
r.uri == uri
|
19
27
|
end
|
20
|
-
|
21
|
-
unless
|
28
|
+
|
29
|
+
unless resource
|
22
30
|
return {
|
23
31
|
isError: true,
|
24
32
|
contents: []
|
25
33
|
}
|
26
34
|
end
|
27
|
-
|
28
|
-
|
35
|
+
|
36
|
+
if resource.respond_to?(:visible?) && !resource.visible?
|
29
37
|
return {
|
30
38
|
isError: true,
|
31
39
|
contents: []
|
32
40
|
}
|
33
41
|
end
|
34
42
|
|
35
|
-
resource = resource_class.new
|
36
|
-
|
37
43
|
begin
|
38
|
-
if content = resource.text
|
44
|
+
if resource.respond_to?(:text) && content = resource.text
|
39
45
|
return {
|
40
46
|
contents: [
|
41
47
|
{
|
42
48
|
uri:,
|
43
|
-
mimeType:
|
49
|
+
mimeType: resource.mime_type,
|
44
50
|
text: formatted(content)
|
45
51
|
}
|
46
52
|
]
|
47
53
|
}
|
48
|
-
elsif content = resource.blob
|
54
|
+
elsif content = resource.blob
|
49
55
|
return {
|
50
56
|
contents: [
|
51
57
|
{
|
52
58
|
uri:,
|
53
|
-
mimeType:
|
59
|
+
mimeType: resource.mime_type,
|
54
60
|
blob: Base64.strict_encode64(content)
|
55
61
|
}
|
56
62
|
]
|
@@ -64,7 +70,7 @@ module ActiveMcp
|
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
67
|
-
def
|
73
|
+
def formatted(object)
|
68
74
|
case object
|
69
75
|
when String
|
70
76
|
object
|
data/app/{models/active_mcp/tool_executor.rb → controllers/concerns/active_mcp/tool_executable.rb}
RENAMED
@@ -1,6 +1,10 @@
|
|
1
1
|
module ActiveMcp
|
2
|
-
|
3
|
-
|
2
|
+
module ToolExecutable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def execute_tool(params:, auth_info:)
|
4
8
|
if params[:jsonrpc].present?
|
5
9
|
tool_name = params[:params][:name]
|
6
10
|
tool_params = params[:params][:arguments]
|
@@ -105,7 +109,7 @@ module ActiveMcp
|
|
105
109
|
end
|
106
110
|
end
|
107
111
|
|
108
|
-
def
|
112
|
+
def formatted(object)
|
109
113
|
case object
|
110
114
|
when String
|
111
115
|
object
|
@@ -3,8 +3,22 @@ json.id @id if @format == :jsonrpc && @id.present?
|
|
3
3
|
|
4
4
|
if @format == :jsonrpc
|
5
5
|
json.result do
|
6
|
-
json.resources
|
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
|
7
14
|
end
|
8
15
|
else
|
9
|
-
json.result
|
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
|
10
24
|
end
|
data/lib/active_mcp/version.rb
CHANGED
data/lib/active_mcp.rb
CHANGED
@@ -1,37 +1,51 @@
|
|
1
|
-
class <%= class_name %>
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
5
25
|
|
6
26
|
# Uncomment and modify this method to implement authorization control
|
7
27
|
# This controls who can see and use this tool
|
8
|
-
# def
|
28
|
+
# def visible?
|
9
29
|
# # Example: require authentication
|
10
|
-
# # return false unless auth_info
|
30
|
+
# # return false unless @auth_info
|
11
31
|
#
|
12
32
|
# # Example: require a specific authentication type
|
13
|
-
# # return false unless auth_info[:type] == :bearer
|
33
|
+
# # return false unless @auth_info[:type] == :bearer
|
14
34
|
#
|
15
35
|
# # Example: check for admin permissions
|
16
36
|
# # admin_tokens = ["admin-token"]
|
17
|
-
# # return admin_tokens.include?(auth_info[:token])
|
37
|
+
# # return admin_tokens.include?(@auth_info[:token])
|
18
38
|
#
|
19
39
|
# # Default: allow all access
|
20
40
|
# true
|
21
41
|
# end
|
22
42
|
|
23
|
-
def text
|
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
|
-
|
43
|
+
def text
|
30
44
|
# Return a string, hash, or any JSON-serializable object
|
31
45
|
{ foo: "bar" }
|
32
46
|
end
|
33
47
|
|
34
|
-
# def blob
|
48
|
+
# def blob
|
35
49
|
# File.read("/path/to/file")
|
36
50
|
# end
|
37
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.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moeki Kawakami
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-07 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -68,9 +68,9 @@ files:
|
|
68
68
|
- README.md
|
69
69
|
- Rakefile
|
70
70
|
- app/controllers/active_mcp/base_controller.rb
|
71
|
-
- app/controllers/concerns/active_mcp/
|
72
|
-
- app/
|
73
|
-
- app/
|
71
|
+
- app/controllers/concerns/active_mcp/request_handlable.rb
|
72
|
+
- app/controllers/concerns/active_mcp/resource_readable.rb
|
73
|
+
- app/controllers/concerns/active_mcp/tool_executable.rb
|
74
74
|
- app/views/active_mcp/cancelled.json.jbuilder
|
75
75
|
- app/views/active_mcp/initialize.json.jbuilder
|
76
76
|
- app/views/active_mcp/initialized.json.jbuilder
|
@@ -83,7 +83,6 @@ files:
|
|
83
83
|
- lib/active_mcp.rb
|
84
84
|
- lib/active_mcp/configuration.rb
|
85
85
|
- lib/active_mcp/engine.rb
|
86
|
-
- lib/active_mcp/resource.rb
|
87
86
|
- lib/active_mcp/server.rb
|
88
87
|
- lib/active_mcp/server/error_codes.rb
|
89
88
|
- lib/active_mcp/server/method.rb
|
data/lib/active_mcp/resource.rb
DELETED
@@ -1,64 +0,0 @@
|
|
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
|