active_mcp 0.7.0 → 0.8.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 +47 -17
- data/app/controllers/concerns/active_mcp/request_handlable.rb +10 -0
- data/app/controllers/concerns/active_mcp/resource_readable.rb +2 -2
- data/app/views/active_mcp/completion_complete.json.jbuilder +16 -0
- data/app/views/active_mcp/resources_list.json.jbuilder +2 -2
- data/lib/active_mcp/completion.rb +18 -0
- data/lib/active_mcp/resource/base.rb +29 -0
- data/lib/active_mcp/schema/base.rb +6 -6
- data/lib/active_mcp/server/method.rb +1 -0
- data/lib/active_mcp/server/protocol_handler.rb +17 -0
- data/lib/active_mcp/version.rb +1 -1
- data/lib/active_mcp.rb +2 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 664064f2b6638585937706546194313e1b9beddcec7952bde94b34b39e39f851
|
4
|
+
data.tar.gz: d97d6e74d9a1235fa99f7362e0c38102069705b7c6ccb8f45d292ed6f45f08f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd746883f2345681dafaf3ba99d1925c11eb8bf0a2ee2dc25fd17d4f1c3906b371f961a64298edc998cc58b6c1424a55efdfbbd0195c5e6e7a21dd160376821d
|
7
|
+
data.tar.gz: dd3ccaa91bd1f807d6f2ea7fc9dc03dc363b30b4bcad3335448f34d978c70f98d63b0ac4641455e9600639d2b88c94ca6c47cd945c8a82edecc5577b4ecbe65c
|
data/README.md
CHANGED
@@ -339,7 +339,7 @@ MCP Resources allow you to share data and files with AI assistants. Resources ha
|
|
339
339
|
Resources are Ruby classes `**Resource`:
|
340
340
|
|
341
341
|
```ruby
|
342
|
-
class UserResource
|
342
|
+
class UserResource < ActiveMcp::Resource::Base
|
343
343
|
def initialize(id:)
|
344
344
|
@user = User.find(id)
|
345
345
|
@auth_info = auth_info
|
@@ -401,7 +401,13 @@ end
|
|
401
401
|
2. **Binary Content** - Use the `blob` method to return binary files:
|
402
402
|
|
403
403
|
```ruby
|
404
|
-
class ImageResource
|
404
|
+
class ImageResource < ActiveMcp::Resource::Base
|
405
|
+
class << self
|
406
|
+
def mime_type
|
407
|
+
"image/png"
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
405
411
|
def name
|
406
412
|
"Profile Image"
|
407
413
|
end
|
@@ -410,10 +416,6 @@ class ImageResource
|
|
410
416
|
"data://localhost/image"
|
411
417
|
end
|
412
418
|
|
413
|
-
def mime_type
|
414
|
-
"image/png"
|
415
|
-
end
|
416
|
-
|
417
419
|
def description
|
418
420
|
"Profile image"
|
419
421
|
end
|
@@ -446,32 +448,60 @@ MCP Resource Teamplates allow you to define template of resources.
|
|
446
448
|
Resources are Ruby classes `**ResourceTemplates`:
|
447
449
|
|
448
450
|
```ruby
|
449
|
-
class
|
450
|
-
|
451
|
-
|
451
|
+
class UserResource < ActiveMcp::Resource::Base
|
452
|
+
class << self
|
453
|
+
def name
|
454
|
+
"Users"
|
455
|
+
end
|
456
|
+
|
457
|
+
def uri_template
|
458
|
+
"data://localhost/users/{id}"
|
459
|
+
end
|
460
|
+
|
461
|
+
def mime_type
|
462
|
+
"application/json"
|
463
|
+
end
|
464
|
+
|
465
|
+
def description
|
466
|
+
"This is a test."
|
467
|
+
end
|
468
|
+
|
469
|
+
def visible?(context:)
|
470
|
+
# Your logic...
|
471
|
+
end
|
452
472
|
end
|
453
473
|
|
454
|
-
|
455
|
-
|
474
|
+
argument :id, ->(value) do
|
475
|
+
User.all.pluck(:id).filter { _1.match(value) }
|
456
476
|
end
|
457
477
|
|
458
|
-
def
|
459
|
-
|
478
|
+
def initialize(id:)
|
479
|
+
@user = User.find(id)
|
480
|
+
end
|
481
|
+
|
482
|
+
def name
|
483
|
+
@user.name
|
460
484
|
end
|
461
485
|
|
462
486
|
def description
|
463
|
-
|
487
|
+
@user.profile
|
464
488
|
end
|
465
489
|
|
466
|
-
def
|
467
|
-
|
490
|
+
def uri
|
491
|
+
"data://localhost/users/#{@user.name}"
|
492
|
+
end
|
493
|
+
|
494
|
+
def text
|
495
|
+
{ name: @user.name }
|
468
496
|
end
|
469
497
|
end
|
470
498
|
```
|
471
499
|
|
472
500
|
```ruby
|
473
501
|
class MySchema < ActiveMcp::Schema::Base
|
474
|
-
|
502
|
+
User.all.each do |user|
|
503
|
+
resource UserResource.new(id: user.id)
|
504
|
+
end
|
475
505
|
end
|
476
506
|
```
|
477
507
|
|
@@ -40,6 +40,11 @@ module ActiveMcp
|
|
40
40
|
@tool_result = execute_tool(params:, context:)
|
41
41
|
@format = :jsonrpc
|
42
42
|
render 'active_mcp/tools_call', formats: :json
|
43
|
+
when Method::COMPLETION_COMPLETE
|
44
|
+
type = params.dig(:params, :ref, :type)
|
45
|
+
@completion = ActiveMcp::Completion.new.complete(params: params[:params], context:, refs: type === "ref/resource" ? schema.resource_templates : [])
|
46
|
+
@format = :jsonrpc
|
47
|
+
render "active_mcp/completion_complete", formats: :json
|
43
48
|
else
|
44
49
|
@format = :jsonrpc
|
45
50
|
render 'active_mcp/no_method', formats: :json
|
@@ -68,6 +73,11 @@ module ActiveMcp
|
|
68
73
|
@tool_result = execute_tool(params:, context:)
|
69
74
|
@format = :json
|
70
75
|
render 'active_mcp/tools_call', formats: :json
|
76
|
+
when Method::COMPLETION_COMPLETE
|
77
|
+
type = params.dig(:params, :ref, :type)
|
78
|
+
@completion = ActiveMcp::Completion.new.complete(params: params[:params], context:, refs: type == "ref/resource" ? schema.resource_templates : [])
|
79
|
+
@format = :json
|
80
|
+
render "active_mcp/completion_complete", formats: :json
|
71
81
|
else
|
72
82
|
@format = :json
|
73
83
|
render 'active_mcp/no_method', formats: :json
|
@@ -42,7 +42,7 @@ module ActiveMcp
|
|
42
42
|
contents: [
|
43
43
|
{
|
44
44
|
uri:,
|
45
|
-
mimeType: resource.mime_type,
|
45
|
+
mimeType: resource.class.mime_type,
|
46
46
|
text: formatted(content)
|
47
47
|
}
|
48
48
|
]
|
@@ -52,7 +52,7 @@ module ActiveMcp
|
|
52
52
|
contents: [
|
53
53
|
{
|
54
54
|
uri:,
|
55
|
-
mimeType: resource.mime_type,
|
55
|
+
mimeType: resource.class.mime_type,
|
56
56
|
blob: Base64.strict_encode64(content)
|
57
57
|
}
|
58
58
|
]
|
@@ -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 do
|
6
|
+
json.completion do
|
7
|
+
json.values @completion[:values]
|
8
|
+
json.total @completion[:total]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
else
|
12
|
+
json.result do
|
13
|
+
json.values @completion[:values]
|
14
|
+
json.total @completion[:total]
|
15
|
+
end
|
16
|
+
end
|
@@ -7,7 +7,7 @@ if @format == :jsonrpc
|
|
7
7
|
json.array!(@resources) do |resource|
|
8
8
|
json.name resource.name
|
9
9
|
json.uri resource.uri
|
10
|
-
json.mimeType resource.mime_type
|
10
|
+
json.mimeType resource.class.mime_type
|
11
11
|
json.description resource.description
|
12
12
|
end
|
13
13
|
end
|
@@ -17,7 +17,7 @@ else
|
|
17
17
|
json.array!(@resources) do |resource|
|
18
18
|
json.name resource.name
|
19
19
|
json.uri resource.uri
|
20
|
-
json.mimeType resource.mime_type
|
20
|
+
json.mimeType resource.class.mime_type
|
21
21
|
json.description resource.description
|
22
22
|
end
|
23
23
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveMcp
|
2
|
+
class Completion
|
3
|
+
def complete(params: {}, context: {}, refs: [])
|
4
|
+
ref_name = params.dig(:ref, :name)
|
5
|
+
uri_template = params.dig(:ref, :uri)
|
6
|
+
arg_name = params.dig(:argument, :name)
|
7
|
+
value = params.dig(:argument, :value)
|
8
|
+
|
9
|
+
if uri_template
|
10
|
+
resource_class = refs.find { _1.uri_template == uri_template }
|
11
|
+
values = resource_class.arguments[arg_name.to_sym].call(value)
|
12
|
+
{ values:, total: values.length }
|
13
|
+
elsif ref_name
|
14
|
+
{ values: [], total: 0 }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "json-schema"
|
2
|
+
|
3
|
+
module ActiveMcp
|
4
|
+
module Resource
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
attr_reader :schema, :arguments
|
8
|
+
|
9
|
+
def argument(name, complete)
|
10
|
+
@arguments = {}
|
11
|
+
@arguments[name] = complete
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
end
|
20
|
+
|
21
|
+
def description
|
22
|
+
end
|
23
|
+
|
24
|
+
def visible?(context: {})
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -7,11 +7,11 @@ module ActiveMcp
|
|
7
7
|
def resource(klass)
|
8
8
|
@resources ||= []
|
9
9
|
@resources << klass
|
10
|
-
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
if klass.class.respond_to?(:uri_template)
|
12
|
+
@resource_templates ||= []
|
13
|
+
@resource_templates << klass.class unless klass.class.in?(@resource_templates)
|
14
|
+
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def tool(klass)
|
@@ -31,8 +31,8 @@ module ActiveMcp
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def resource_templates
|
34
|
-
self.class.resource_templates.filter do |
|
35
|
-
!
|
34
|
+
self.class.resource_templates.filter do |template|
|
35
|
+
!template.respond_to?(:visible?) || template.visible?(context: @context)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -58,6 +58,8 @@ module ActiveMcp
|
|
58
58
|
handle_call_tool(request)
|
59
59
|
when Method::RESOURCES_READ
|
60
60
|
handle_read_resource(request)
|
61
|
+
when Method::COMPLETION_COMPLETE
|
62
|
+
handle_complete(request)
|
61
63
|
else
|
62
64
|
error_response(request[:id], ErrorCode::METHOD_NOT_FOUND, "Unknown method: #{request[:method]}")
|
63
65
|
end
|
@@ -194,6 +196,21 @@ module ActiveMcp
|
|
194
196
|
end
|
195
197
|
end
|
196
198
|
|
199
|
+
def handle_complete(request)
|
200
|
+
begin
|
201
|
+
result = @server.fetch(
|
202
|
+
params: {
|
203
|
+
method: Method::COMPLETION_COMPLETE,
|
204
|
+
params: request[:params],
|
205
|
+
}
|
206
|
+
)
|
207
|
+
success_response(request[:id], { completion: result[:result] })
|
208
|
+
rescue => e
|
209
|
+
Server.("Error reading resource #{uri}", e)
|
210
|
+
error_response(request[:id], ErrorCode::INTERNAL_ERROR, "An error occurred while reading the resource")
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
197
214
|
def success_response(id, result)
|
198
215
|
{
|
199
216
|
jsonrpc: JSON_RPC_VERSION,
|
data/lib/active_mcp/version.rb
CHANGED
data/lib/active_mcp.rb
CHANGED
@@ -5,7 +5,9 @@ require_relative "active_mcp/version"
|
|
5
5
|
require_relative "active_mcp/configuration"
|
6
6
|
require_relative "active_mcp/schema/base"
|
7
7
|
require_relative "active_mcp/tool/base"
|
8
|
+
require_relative "active_mcp/resource/base"
|
8
9
|
require_relative "active_mcp/server"
|
10
|
+
require_relative "active_mcp/completion"
|
9
11
|
|
10
12
|
if defined? ::Rails
|
11
13
|
require_relative "active_mcp/engine"
|
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.8.0
|
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-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- app/controllers/concerns/active_mcp/resource_readable.rb
|
74
74
|
- app/controllers/concerns/active_mcp/tool_executable.rb
|
75
75
|
- app/views/active_mcp/cancelled.json.jbuilder
|
76
|
+
- app/views/active_mcp/completion_complete.json.jbuilder
|
76
77
|
- app/views/active_mcp/initialize.json.jbuilder
|
77
78
|
- app/views/active_mcp/initialized.json.jbuilder
|
78
79
|
- app/views/active_mcp/no_method.json.jbuilder
|
@@ -83,9 +84,11 @@ files:
|
|
83
84
|
- app/views/active_mcp/tools_list.json.jbuilder
|
84
85
|
- config/routes.rb
|
85
86
|
- lib/active_mcp.rb
|
87
|
+
- lib/active_mcp/completion.rb
|
86
88
|
- lib/active_mcp/configuration.rb
|
87
89
|
- lib/active_mcp/controller/base.rb
|
88
90
|
- lib/active_mcp/engine.rb
|
91
|
+
- lib/active_mcp/resource/base.rb
|
89
92
|
- lib/active_mcp/schema/base.rb
|
90
93
|
- lib/active_mcp/server.rb
|
91
94
|
- lib/active_mcp/server/error_codes.rb
|