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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9367d905ac3b7dfa87f27063c41f24f1ed93de9d88a5fc009010ed35bfafa85
4
- data.tar.gz: 7b1ed7d1f79ad9487c92a4e0595727cc60125c3584ae7029be8d3e86f9c2537b
3
+ metadata.gz: 664064f2b6638585937706546194313e1b9beddcec7952bde94b34b39e39f851
4
+ data.tar.gz: d97d6e74d9a1235fa99f7362e0c38102069705b7c6ccb8f45d292ed6f45f08f3
5
5
  SHA512:
6
- metadata.gz: 5f429c060c395a06e9e146c68ea859a1750a00add2b28ba5fe3a968b20b8b335fcc3908a1f89b63aeaf24f092942af9edd4979eec2a3b380d431519ee35623d4
7
- data.tar.gz: 1a0bff75ee866fbba70a5549ee867f6260cd8e925a592b9a044b3769a91ba9955487e3798647c7fa6b356e4c9cbfbd3610550a713aa4cee97dfff896c7514c15
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 UserResourceTemplate
450
- def name
451
- "Users"
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
- def uri_template
455
- "data://localhost/users/{id}"
474
+ argument :id, ->(value) do
475
+ User.all.pluck(:id).filter { _1.match(value) }
456
476
  end
457
477
 
458
- def mime_type
459
- "application/json"
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
- "This is a test."
487
+ @user.profile
464
488
  end
465
489
 
466
- def visible?(context:)
467
- # Your logic...
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
- resource_template UserResourceTemplate.new
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
- def resource_template(klass)
13
- @resource_templates ||= []
14
- @resource_templates << klass
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 |tool_resource|
35
- !tool_resource.respond_to?(:visible?) || tool_resource.visible?(context: @context)
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
 
@@ -11,5 +11,6 @@ module ActiveMcp
11
11
  RESOURCES_LIST = "resources/list"
12
12
  RESOURCES_READ = "resources/read"
13
13
  RESOURCES_TEMPLATES_LIST = "resources/templates/list"
14
+ COMPLETION_COMPLETE = "completion/complete"
14
15
  end
15
16
  end
@@ -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,
@@ -1,3 +1,3 @@
1
1
  module ActiveMcp
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
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.7.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-07 00:00:00.000000000 Z
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