active_mcp 0.8.0 → 0.9.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 +73 -8
- data/app/controllers/concerns/active_mcp/request_handlable.rb +18 -2
- data/app/controllers/concerns/active_mcp/resource_readable.rb +2 -13
- data/app/controllers/concerns/active_mcp/tool_executable.rb +1 -1
- data/app/views/active_mcp/prompts_get.json.jbuilder +12 -0
- data/app/views/active_mcp/prompts_list.json.jbuilder +22 -0
- data/app/views/active_mcp/resource_templates_list.json.jbuilder +2 -2
- data/app/views/active_mcp/resources_list.json.jbuilder +2 -2
- data/app/views/active_mcp/tools_list.json.jbuilder +2 -2
- data/lib/active_mcp/completion.rb +3 -1
- data/lib/active_mcp/engine.rb +1 -0
- data/lib/active_mcp/message/audio.rb +22 -0
- data/lib/active_mcp/message/image.rb +22 -0
- data/lib/active_mcp/message/resource.rb +24 -0
- data/lib/active_mcp/message/text.rb +20 -0
- data/lib/active_mcp/prompt/base.rb +37 -0
- data/lib/active_mcp/resource/base.rb +21 -1
- data/lib/active_mcp/schema/base.rb +15 -4
- data/lib/active_mcp/server/method.rb +2 -0
- data/lib/active_mcp/server/protocol_handler.rb +40 -19
- data/lib/active_mcp/tool/base.rb +1 -1
- data/lib/active_mcp/version.rb +1 -1
- data/lib/active_mcp.rb +5 -1
- data/lib/generators/active_mcp/resource/templates/resource.rb.erb +1 -1
- data/lib/generators/active_mcp/tool/templates/tool.rb.erb +1 -1
- metadata +8 -2
- data/lib/active_mcp/controller/base.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de9c4899c65706f1fb1281e3ac52c7eb5399e4ac6e0224dce35e0ef532779df7
|
4
|
+
data.tar.gz: ad0abe6c7af1b985260fbea629302fa5bcb69f1ac1c94455ccb187e771664691
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8416f28a91cfbc836a78e05b5790b70e0236450f6d901f48df2070dae02095f5e8f436cb39f05dc2a4edd57240c5c8bba99e1a7a32b365c04cac6ec5ed32faaa
|
7
|
+
data.tar.gz: b3cacc88a0649c82959aaf7fa9b3f39fc0d9b9f3c2c1135ad82282667c92ecda8ce71ddc84d7133694a790fcb6a69caa3c45c48f7196662d4952e9c56d304aba
|
data/README.md
CHANGED
@@ -36,6 +36,8 @@ A Ruby on Rails engine for the [Model Context Protocol (MCP)](https://modelconte
|
|
36
36
|
- [Resource Types](#resource-types)
|
37
37
|
- [📦 MCP Resource Templates](#-mcp-resource-templates)
|
38
38
|
- [Creating Resource Templates](#creating-resource-templates)
|
39
|
+
- [💬 MCP Prompts](#-mcp-prompts)
|
40
|
+
- [Creating Prompt](#creating-prompt)
|
39
41
|
- [💡 Best Practices](#-best-practices)
|
40
42
|
- [1. Create Specific Tool Classes](#1-create-specific-tool-classes)
|
41
43
|
- [2. Validate and Sanitize Inputs](#2-validate-and-sanitize-inputs)
|
@@ -92,7 +94,7 @@ $ rails generate active_mcp:tool create_note
|
|
92
94
|
|
93
95
|
```ruby
|
94
96
|
class CreateNoteTool < ActiveMcp::Tool::Base
|
95
|
-
def
|
97
|
+
def tool_name
|
96
98
|
"Create Note"
|
97
99
|
end
|
98
100
|
|
@@ -122,7 +124,7 @@ end
|
|
122
124
|
4. Create controller ans set up routing:
|
123
125
|
|
124
126
|
```ruby
|
125
|
-
class MyMcpController < ActiveMcp::
|
127
|
+
class MyMcpController < ActiveMcp::BaseController
|
126
128
|
def schema
|
127
129
|
MySchema.new(context:)
|
128
130
|
end
|
@@ -213,7 +215,7 @@ MCP tools are Ruby classes that inherit from `ActiveMcp::Tool::Base` and define
|
|
213
215
|
|
214
216
|
```ruby
|
215
217
|
class SearchUsersTool < ActiveMcp::Tool::Base
|
216
|
-
def
|
218
|
+
def tool_name
|
217
219
|
"Search Users"
|
218
220
|
end
|
219
221
|
|
@@ -268,7 +270,7 @@ Control access to tools by overriding the `visible?` class method:
|
|
268
270
|
|
269
271
|
```ruby
|
270
272
|
class AdminOnlyTool < ActiveMcp::Tool::Base
|
271
|
-
def
|
273
|
+
def tool_name
|
272
274
|
"Admin-only tool"
|
273
275
|
end
|
274
276
|
|
@@ -345,7 +347,7 @@ class UserResource < ActiveMcp::Resource::Base
|
|
345
347
|
@auth_info = auth_info
|
346
348
|
end
|
347
349
|
|
348
|
-
def
|
350
|
+
def resource_name
|
349
351
|
@user.name
|
350
352
|
end
|
351
353
|
|
@@ -408,7 +410,7 @@ class ImageResource < ActiveMcp::Resource::Base
|
|
408
410
|
end
|
409
411
|
end
|
410
412
|
|
411
|
-
def
|
413
|
+
def resource_name
|
412
414
|
"Profile Image"
|
413
415
|
end
|
414
416
|
|
@@ -450,7 +452,7 @@ Resources are Ruby classes `**ResourceTemplates`:
|
|
450
452
|
```ruby
|
451
453
|
class UserResource < ActiveMcp::Resource::Base
|
452
454
|
class << self
|
453
|
-
def
|
455
|
+
def resource_template_name
|
454
456
|
"Users"
|
455
457
|
end
|
456
458
|
|
@@ -479,7 +481,7 @@ class UserResource < ActiveMcp::Resource::Base
|
|
479
481
|
@user = User.find(id)
|
480
482
|
end
|
481
483
|
|
482
|
-
def
|
484
|
+
def resource_name
|
483
485
|
@user.name
|
484
486
|
end
|
485
487
|
|
@@ -505,6 +507,69 @@ class MySchema < ActiveMcp::Schema::Base
|
|
505
507
|
end
|
506
508
|
```
|
507
509
|
|
510
|
+
## 💬 MCP Prompts
|
511
|
+
|
512
|
+
MCP Prompts allow you to define prompt set.
|
513
|
+
|
514
|
+
### Creating Prompt
|
515
|
+
|
516
|
+
Resources are Ruby classes `**Prompt`:
|
517
|
+
|
518
|
+
```ruby
|
519
|
+
class HelloPrompt < ActiveMcp::Prompt::Base
|
520
|
+
class << self
|
521
|
+
def prompt_name
|
522
|
+
"hello"
|
523
|
+
end
|
524
|
+
|
525
|
+
def description
|
526
|
+
"This is a test."
|
527
|
+
end
|
528
|
+
|
529
|
+
def visible?(context:)
|
530
|
+
# Your logic...
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
argument :name, ->(value) do
|
535
|
+
User.all.pluck(:name).filter { _1.match(value) }
|
536
|
+
end
|
537
|
+
|
538
|
+
def initialize(name:)
|
539
|
+
@name = name
|
540
|
+
end
|
541
|
+
|
542
|
+
def messages
|
543
|
+
[
|
544
|
+
ActiveMcp::Message::Text.new(
|
545
|
+
role: "user",
|
546
|
+
text: "Hello! #{@name}"
|
547
|
+
),
|
548
|
+
ActiveMcp::Message::Image.new(
|
549
|
+
role: "assistant",
|
550
|
+
data: File.read(file),
|
551
|
+
mime_type: "image/png"
|
552
|
+
),
|
553
|
+
ActiveMcp::Message::Audio.new(
|
554
|
+
role: "user",
|
555
|
+
data: File.read(file),
|
556
|
+
mime_type: "audio/mpeg"
|
557
|
+
),
|
558
|
+
ActiveMcp::Message::Resource.new(
|
559
|
+
role: "assistant",
|
560
|
+
resource: UserResource.new(name: @name)
|
561
|
+
)
|
562
|
+
]
|
563
|
+
end
|
564
|
+
end
|
565
|
+
```
|
566
|
+
|
567
|
+
```ruby
|
568
|
+
class MySchema < ActiveMcp::Schema::Base
|
569
|
+
prompt HelloPrompt
|
570
|
+
end
|
571
|
+
```
|
572
|
+
|
508
573
|
## 💡 Best Practices
|
509
574
|
|
510
575
|
### 1. Create Specific Tool Classes
|
@@ -42,9 +42,17 @@ module ActiveMcp
|
|
42
42
|
render 'active_mcp/tools_call', formats: :json
|
43
43
|
when Method::COMPLETION_COMPLETE
|
44
44
|
type = params.dig(:params, :ref, :type)
|
45
|
-
@completion = ActiveMcp::Completion.new.complete(params: params[:params], context:, refs: type === "ref/resource" ? schema.resource_templates :
|
45
|
+
@completion = ActiveMcp::Completion.new.complete(params: params[:params], context:, refs: type === "ref/resource" ? schema.resource_templates : schema.prompts)
|
46
46
|
@format = :jsonrpc
|
47
47
|
render "active_mcp/completion_complete", formats: :json
|
48
|
+
when Method::PROMPTS_LIST
|
49
|
+
@prompts = schema.prompts
|
50
|
+
@format = :jsonrpc
|
51
|
+
render 'active_mcp/prompts_list', formats: :json
|
52
|
+
when Method::PROMPTS_GET
|
53
|
+
@prompt = schema.prompts.find { _1.prompt_name == params[:params][:name] }.new(**params[:params][:arguments].permit!.to_h.symbolize_keys)
|
54
|
+
@format = :jsonrpc
|
55
|
+
render 'active_mcp/prompts_get', formats: :json
|
48
56
|
else
|
49
57
|
@format = :jsonrpc
|
50
58
|
render 'active_mcp/no_method', formats: :json
|
@@ -75,9 +83,17 @@ module ActiveMcp
|
|
75
83
|
render 'active_mcp/tools_call', formats: :json
|
76
84
|
when Method::COMPLETION_COMPLETE
|
77
85
|
type = params.dig(:params, :ref, :type)
|
78
|
-
@completion = ActiveMcp::Completion.new.complete(params: params[:params], context:, refs: type == "ref/resource" ? schema.resource_templates :
|
86
|
+
@completion = ActiveMcp::Completion.new.complete(params: params[:params], context:, refs: type == "ref/resource" ? schema.resource_templates : schema.prompts)
|
79
87
|
@format = :json
|
80
88
|
render "active_mcp/completion_complete", formats: :json
|
89
|
+
when Method::PROMPTS_LIST
|
90
|
+
@prompts = schema.prompts
|
91
|
+
@format = :json
|
92
|
+
render 'active_mcp/prompts_list', formats: :json
|
93
|
+
when Method::PROMPTS_GET
|
94
|
+
@prompt = schema.prompts&.find { _1.prompt_name == params[:params][:name] }.new(**params[:params][:arguments].permit!.to_h.symbolize_keys)
|
95
|
+
@format = :json
|
96
|
+
render 'active_mcp/prompts_get', formats: :json
|
81
97
|
else
|
82
98
|
@format = :json
|
83
99
|
render 'active_mcp/no_method', formats: :json
|
@@ -37,13 +37,13 @@ module ActiveMcp
|
|
37
37
|
end
|
38
38
|
|
39
39
|
begin
|
40
|
-
if resource.respond_to?(:text) && content = resource.
|
40
|
+
if resource.respond_to?(:text) && content = resource.content
|
41
41
|
return {
|
42
42
|
contents: [
|
43
43
|
{
|
44
44
|
uri:,
|
45
45
|
mimeType: resource.class.mime_type,
|
46
|
-
text:
|
46
|
+
text: content
|
47
47
|
}
|
48
48
|
]
|
49
49
|
}
|
@@ -65,16 +65,5 @@ module ActiveMcp
|
|
65
65
|
}
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
69
|
-
def formatted(object)
|
70
|
-
case object
|
71
|
-
when String
|
72
|
-
object
|
73
|
-
when Hash
|
74
|
-
object.to_json
|
75
|
-
else
|
76
|
-
object.to_s
|
77
|
-
end
|
78
|
-
end
|
79
68
|
end
|
80
69
|
end
|
@@ -0,0 +1,12 @@
|
|
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.description @prompt.class.description
|
7
|
+
json.messages @prompt.messages.map(&:to_h)
|
8
|
+
end
|
9
|
+
else
|
10
|
+
json.description @prompt.class.description
|
11
|
+
json.messages @prompt.messages.map(&:to_h)
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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.prompts do
|
7
|
+
json.array!(@prompts) do |prompt|
|
8
|
+
json.name prompt.prompt_name
|
9
|
+
json.description prompt.description
|
10
|
+
json.arguments prompt.arguments.map { _1.except(:complete) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
else
|
15
|
+
json.result do
|
16
|
+
json.array!(@prompts) do |prompt|
|
17
|
+
json.name prompt.prompt_name
|
18
|
+
json.description prompt.description
|
19
|
+
json.arguments prompt.arguments.map { _1.except(:complete) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -5,7 +5,7 @@ if @format == :jsonrpc
|
|
5
5
|
json.result do
|
6
6
|
json.resourceTemplates do
|
7
7
|
json.array!(@resource_templates) do |resource|
|
8
|
-
json.name resource.
|
8
|
+
json.name resource.resource_template_name
|
9
9
|
json.uriTemplate resource.uri_template
|
10
10
|
json.mimeType resource.mime_type
|
11
11
|
json.description resource.description
|
@@ -15,7 +15,7 @@ if @format == :jsonrpc
|
|
15
15
|
else
|
16
16
|
json.result do
|
17
17
|
json.array!(@resource_templates) do |resource|
|
18
|
-
json.name resource.
|
18
|
+
json.name resource.resource_template_name
|
19
19
|
json.uriTemplate resource.uri_template
|
20
20
|
json.mimeType resource.mime_type
|
21
21
|
json.description resource.description
|
@@ -5,7 +5,7 @@ if @format == :jsonrpc
|
|
5
5
|
json.result do
|
6
6
|
json.resources do
|
7
7
|
json.array!(@resources) do |resource|
|
8
|
-
json.name resource.
|
8
|
+
json.name resource.resource_name
|
9
9
|
json.uri resource.uri
|
10
10
|
json.mimeType resource.class.mime_type
|
11
11
|
json.description resource.description
|
@@ -15,7 +15,7 @@ if @format == :jsonrpc
|
|
15
15
|
else
|
16
16
|
json.result do
|
17
17
|
json.array!(@resources) do |resource|
|
18
|
-
json.name resource.
|
18
|
+
json.name resource.resource_name
|
19
19
|
json.uri resource.uri
|
20
20
|
json.mimeType resource.class.mime_type
|
21
21
|
json.description resource.description
|
@@ -5,7 +5,7 @@ if @format == :jsonrpc
|
|
5
5
|
json.result do
|
6
6
|
json.tools do
|
7
7
|
json.array!(@tools) do |tool|
|
8
|
-
json.name tool.
|
8
|
+
json.name tool.tool_name
|
9
9
|
json.description tool.description
|
10
10
|
json.inputSchema tool.class.schema
|
11
11
|
end
|
@@ -14,7 +14,7 @@ if @format == :jsonrpc
|
|
14
14
|
else
|
15
15
|
json.result do
|
16
16
|
json.array!(@tools) do |tool|
|
17
|
-
json.name tool.
|
17
|
+
json.name tool.tool_name
|
18
18
|
json.description tool.description
|
19
19
|
json.inputSchema tool.class.schema
|
20
20
|
end
|
@@ -11,7 +11,9 @@ module ActiveMcp
|
|
11
11
|
values = resource_class.arguments[arg_name.to_sym].call(value)
|
12
12
|
{ values:, total: values.length }
|
13
13
|
elsif ref_name
|
14
|
-
{
|
14
|
+
prompt_class = refs.find { _1.prompt_name == ref_name }
|
15
|
+
values = prompt_class.arguments.find { _1[:name] == arg_name.to_sym }[:complete].call(value)
|
16
|
+
{ values:, total: values.length }
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
data/lib/active_mcp/engine.rb
CHANGED
@@ -7,6 +7,7 @@ module ActiveMcp
|
|
7
7
|
Rails.root.join("app", "mcp", "tools"),
|
8
8
|
Rails.root.join("app", "mcp", "resources"),
|
9
9
|
Rails.root.join("app", "mcp", "resource_templates"),
|
10
|
+
Rails.root.join("app", "mcp", "prompts"),
|
10
11
|
Rails.root.join("app", "mcp", "schemas")
|
11
12
|
].each do |tools_path|
|
12
13
|
if Dir.exist?(tools_path)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveMcp
|
2
|
+
module Message
|
3
|
+
class Audio
|
4
|
+
def initialize(role:, data:, mime_type:)
|
5
|
+
@role = role
|
6
|
+
@data = data
|
7
|
+
@mime_type = mime_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
{
|
12
|
+
role: @role,
|
13
|
+
content: {
|
14
|
+
type: "audio",
|
15
|
+
data: Base64.strict_encode64(@data),
|
16
|
+
mimeType: @mime_type
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveMcp
|
2
|
+
module Message
|
3
|
+
class Image
|
4
|
+
def initialize(role:, data:, mime_type:)
|
5
|
+
@role = role
|
6
|
+
@data = data
|
7
|
+
@mime_type = mime_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
{
|
12
|
+
role: @role,
|
13
|
+
content: {
|
14
|
+
type: "image",
|
15
|
+
data: Base64.strict_encode64(@data),
|
16
|
+
mimeType: @mime_type
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveMcp
|
2
|
+
module Message
|
3
|
+
class Resource
|
4
|
+
def initialize(role:, resource:)
|
5
|
+
@role = role
|
6
|
+
@resource = resource
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_h
|
10
|
+
{
|
11
|
+
role: @role,
|
12
|
+
content: {
|
13
|
+
type: "resource",
|
14
|
+
resource: {
|
15
|
+
uri: @resource.uri,
|
16
|
+
mimeType: @resource.class.mime_type,
|
17
|
+
text: @resource.content,
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveMcp
|
2
|
+
module Prompt
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
attr_reader :arguments
|
6
|
+
|
7
|
+
def argument(name, required: false, description: nil, complete: ->(){})
|
8
|
+
@arguments ||= []
|
9
|
+
|
10
|
+
@arguments << {
|
11
|
+
name:,
|
12
|
+
description:,
|
13
|
+
required:,
|
14
|
+
complete:
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(*args, context: {})
|
20
|
+
end
|
21
|
+
|
22
|
+
def prompt_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def description
|
26
|
+
end
|
27
|
+
|
28
|
+
def visible?(context: {})
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def messages
|
33
|
+
raise NotImplementedError, "#{self.class.name}#messages must be implemented"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -6,6 +6,15 @@ module ActiveMcp
|
|
6
6
|
class << self
|
7
7
|
attr_reader :schema, :arguments
|
8
8
|
|
9
|
+
def resource_template_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
end
|
14
|
+
|
15
|
+
def mime_type
|
16
|
+
end
|
17
|
+
|
9
18
|
def argument(name, complete)
|
10
19
|
@arguments = {}
|
11
20
|
@arguments[name] = complete
|
@@ -15,7 +24,7 @@ module ActiveMcp
|
|
15
24
|
def initialize
|
16
25
|
end
|
17
26
|
|
18
|
-
def
|
27
|
+
def resource_name
|
19
28
|
end
|
20
29
|
|
21
30
|
def description
|
@@ -24,6 +33,17 @@ module ActiveMcp
|
|
24
33
|
def visible?(context: {})
|
25
34
|
true
|
26
35
|
end
|
36
|
+
|
37
|
+
def content
|
38
|
+
case text
|
39
|
+
when String
|
40
|
+
text
|
41
|
+
when Hash
|
42
|
+
text.to_json
|
43
|
+
else
|
44
|
+
text.to_s
|
45
|
+
end
|
46
|
+
end
|
27
47
|
end
|
28
48
|
end
|
29
49
|
end
|
@@ -2,7 +2,7 @@ module ActiveMcp
|
|
2
2
|
module Schema
|
3
3
|
class Base
|
4
4
|
class << self
|
5
|
-
attr_reader :resources, :resource_templates, :tools
|
5
|
+
attr_reader :resources, :resource_templates, :tools, :prompts
|
6
6
|
|
7
7
|
def resource(klass)
|
8
8
|
@resources ||= []
|
@@ -18,6 +18,11 @@ module ActiveMcp
|
|
18
18
|
@tools ||= []
|
19
19
|
@tools << klass
|
20
20
|
end
|
21
|
+
|
22
|
+
def prompt(klass)
|
23
|
+
@prompts ||= []
|
24
|
+
@prompts << klass
|
25
|
+
end
|
21
26
|
end
|
22
27
|
|
23
28
|
def initialize(context: {})
|
@@ -25,22 +30,28 @@ module ActiveMcp
|
|
25
30
|
end
|
26
31
|
|
27
32
|
def resources
|
28
|
-
self.class.resources
|
33
|
+
self.class.resources&.filter do |resource|
|
29
34
|
!resource.respond_to?(:visible?) || resource.visible?(context: @context)
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
33
38
|
def resource_templates
|
34
|
-
self.class.resource_templates
|
39
|
+
self.class.resource_templates&.filter do |template|
|
35
40
|
!template.respond_to?(:visible?) || template.visible?(context: @context)
|
36
41
|
end
|
37
42
|
end
|
38
43
|
|
39
44
|
def tools
|
40
|
-
self.class.tools
|
45
|
+
self.class.tools&.filter do |tool|
|
41
46
|
!tool.respond_to?(:visible?) || tool.visible?(context: @context)
|
42
47
|
end
|
43
48
|
end
|
49
|
+
|
50
|
+
def prompts
|
51
|
+
self.class.prompts&.filter do |resource|
|
52
|
+
!resource.respond_to?(:visible?) || resource.visible?(context: @context)
|
53
|
+
end
|
54
|
+
end
|
44
55
|
end
|
45
56
|
end
|
46
57
|
end
|
@@ -31,16 +31,6 @@ module ActiveMcp
|
|
31
31
|
private
|
32
32
|
|
33
33
|
def handle_request(request)
|
34
|
-
allowed_methods = [
|
35
|
-
Method::INITIALIZE,
|
36
|
-
Method::INITIALIZED,
|
37
|
-
Method::PING
|
38
|
-
]
|
39
|
-
|
40
|
-
if !@initialized && !allowed_methods.include?(request[:method])
|
41
|
-
return error_response(request[:id], ErrorCode::NOT_INITIALIZED, "Server not initialized")
|
42
|
-
end
|
43
|
-
|
44
34
|
case request[:method]
|
45
35
|
when Method::INITIALIZE
|
46
36
|
handle_initialize(request)
|
@@ -60,6 +50,10 @@ module ActiveMcp
|
|
60
50
|
handle_read_resource(request)
|
61
51
|
when Method::COMPLETION_COMPLETE
|
62
52
|
handle_complete(request)
|
53
|
+
when Method::PROMPTS_LIST
|
54
|
+
handle_list_prompts(request)
|
55
|
+
when Method::PROMPTS_GET
|
56
|
+
handle_get_prompt(request)
|
63
57
|
else
|
64
58
|
error_response(request[:id], ErrorCode::METHOD_NOT_FOUND, "Unknown method: #{request[:method]}")
|
65
59
|
end
|
@@ -88,13 +82,9 @@ module ActiveMcp
|
|
88
82
|
result: {
|
89
83
|
protocolVersion: PROTOCOL_VERSION,
|
90
84
|
capabilities: {
|
91
|
-
resources: {
|
92
|
-
|
93
|
-
|
94
|
-
},
|
95
|
-
tools: {
|
96
|
-
listChanged: false
|
97
|
-
}
|
85
|
+
resources: {},
|
86
|
+
tools: {},
|
87
|
+
prompts: {},
|
98
88
|
},
|
99
89
|
serverInfo: {
|
100
90
|
name: @server.name,
|
@@ -191,7 +181,7 @@ module ActiveMcp
|
|
191
181
|
|
192
182
|
success_response(request[:id], result)
|
193
183
|
rescue => e
|
194
|
-
Server.("Error reading resource #{uri}", e)
|
184
|
+
Server.log_error("Error reading resource #{uri}", e)
|
195
185
|
error_response(request[:id], ErrorCode::INTERNAL_ERROR, "An error occurred while reading the resource")
|
196
186
|
end
|
197
187
|
end
|
@@ -206,7 +196,38 @@ module ActiveMcp
|
|
206
196
|
)
|
207
197
|
success_response(request[:id], { completion: result[:result] })
|
208
198
|
rescue => e
|
209
|
-
Server.("Error reading resource #{uri}", e)
|
199
|
+
Server.log_error("Error reading resource #{uri}", e)
|
200
|
+
error_response(request[:id], ErrorCode::INTERNAL_ERROR, "An error occurred while reading the resource")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def handle_list_prompts(request)
|
205
|
+
begin
|
206
|
+
result = @server.fetch(params: { method: Method::PROMPTS_LIST })
|
207
|
+
success_response(request[:id], { prompts: result[:result] })
|
208
|
+
rescue => e
|
209
|
+
Server.log_error("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
|
+
|
214
|
+
def handle_get_prompt(request)
|
215
|
+
name = request.dig(:params, :name)
|
216
|
+
arguments = request.dig(:params, :arguments)
|
217
|
+
begin
|
218
|
+
result = @server.fetch(
|
219
|
+
params: {
|
220
|
+
method: Method::PROMPTS_GET,
|
221
|
+
params: {
|
222
|
+
name:,
|
223
|
+
arguments:,
|
224
|
+
}
|
225
|
+
}
|
226
|
+
)
|
227
|
+
|
228
|
+
success_response(request[:id], result)
|
229
|
+
rescue => e
|
230
|
+
Server.log_error("Error reading resource #{uri}", e)
|
210
231
|
error_response(request[:id], ErrorCode::INTERNAL_ERROR, "An error occurred while reading the resource")
|
211
232
|
end
|
212
233
|
end
|
data/lib/active_mcp/tool/base.rb
CHANGED
data/lib/active_mcp/version.rb
CHANGED
data/lib/active_mcp.rb
CHANGED
@@ -6,12 +6,16 @@ require_relative "active_mcp/configuration"
|
|
6
6
|
require_relative "active_mcp/schema/base"
|
7
7
|
require_relative "active_mcp/tool/base"
|
8
8
|
require_relative "active_mcp/resource/base"
|
9
|
+
require_relative "active_mcp/prompt/base"
|
10
|
+
require_relative "active_mcp/message/text"
|
11
|
+
require_relative "active_mcp/message/image"
|
12
|
+
require_relative "active_mcp/message/audio"
|
13
|
+
require_relative "active_mcp/message/resource"
|
9
14
|
require_relative "active_mcp/server"
|
10
15
|
require_relative "active_mcp/completion"
|
11
16
|
|
12
17
|
if defined? ::Rails
|
13
18
|
require_relative "active_mcp/engine"
|
14
|
-
require_relative "active_mcp/controller/base"
|
15
19
|
end
|
16
20
|
|
17
21
|
module ActiveMcp
|
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.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moeki Kawakami
|
@@ -77,6 +77,8 @@ files:
|
|
77
77
|
- app/views/active_mcp/initialize.json.jbuilder
|
78
78
|
- app/views/active_mcp/initialized.json.jbuilder
|
79
79
|
- app/views/active_mcp/no_method.json.jbuilder
|
80
|
+
- app/views/active_mcp/prompts_get.json.jbuilder
|
81
|
+
- app/views/active_mcp/prompts_list.json.jbuilder
|
80
82
|
- app/views/active_mcp/resource_templates_list.json.jbuilder
|
81
83
|
- app/views/active_mcp/resources_list.json.jbuilder
|
82
84
|
- app/views/active_mcp/resources_read.json.jbuilder
|
@@ -86,8 +88,12 @@ files:
|
|
86
88
|
- lib/active_mcp.rb
|
87
89
|
- lib/active_mcp/completion.rb
|
88
90
|
- lib/active_mcp/configuration.rb
|
89
|
-
- lib/active_mcp/controller/base.rb
|
90
91
|
- lib/active_mcp/engine.rb
|
92
|
+
- lib/active_mcp/message/audio.rb
|
93
|
+
- lib/active_mcp/message/image.rb
|
94
|
+
- lib/active_mcp/message/resource.rb
|
95
|
+
- lib/active_mcp/message/text.rb
|
96
|
+
- lib/active_mcp/prompt/base.rb
|
91
97
|
- lib/active_mcp/resource/base.rb
|
92
98
|
- lib/active_mcp/schema/base.rb
|
93
99
|
- lib/active_mcp/server.rb
|