mcp 0.4.0 → 0.5.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/.github/workflows/ci.yml +22 -7
- data/.github/workflows/release.yml +34 -2
- data/.rubocop.yml +3 -0
- data/AGENTS.md +2 -14
- data/CHANGELOG.md +28 -0
- data/Gemfile +6 -4
- data/README.md +66 -30
- data/RELEASE.md +12 -0
- data/bin/generate-gh-pages.sh +119 -0
- data/dev.yml +1 -2
- data/docs/_config.yml +6 -0
- data/docs/index.md +7 -0
- data/docs/latest/index.html +19 -0
- data/examples/http_server.rb +0 -1
- data/examples/streamable_http_server.rb +0 -1
- data/lib/json_rpc_handler.rb +151 -0
- data/lib/mcp/client/http.rb +23 -7
- data/lib/mcp/client.rb +29 -0
- data/lib/mcp/configuration.rb +11 -9
- data/lib/mcp/content.rb +2 -2
- data/lib/mcp/icon.rb +22 -0
- data/lib/mcp/instrumentation.rb +1 -1
- data/lib/mcp/methods.rb +3 -0
- data/lib/mcp/prompt/message.rb +1 -1
- data/lib/mcp/prompt/result.rb +1 -1
- data/lib/mcp/prompt.rb +14 -2
- data/lib/mcp/resource/contents.rb +1 -1
- data/lib/mcp/resource/embedded.rb +1 -1
- data/lib/mcp/resource.rb +4 -2
- data/lib/mcp/resource_template.rb +4 -2
- data/lib/mcp/server/transports/streamable_http_transport.rb +85 -15
- data/lib/mcp/server.rb +89 -23
- data/lib/mcp/string_utils.rb +3 -3
- data/lib/mcp/tool/annotations.rb +1 -1
- data/lib/mcp/tool/input_schema.rb +6 -55
- data/lib/mcp/tool/output_schema.rb +3 -54
- data/lib/mcp/tool/response.rb +1 -1
- data/lib/mcp/tool/schema.rb +65 -0
- data/lib/mcp/tool.rb +34 -5
- data/lib/mcp/version.rb +1 -1
- data/lib/mcp.rb +2 -0
- data/mcp.gemspec +5 -2
- metadata +14 -17
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module JsonRpcHandler
|
|
6
|
+
class Version
|
|
7
|
+
V1_0 = "1.0"
|
|
8
|
+
V2_0 = "2.0"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class ErrorCode
|
|
12
|
+
INVALID_REQUEST = -32600
|
|
13
|
+
METHOD_NOT_FOUND = -32601
|
|
14
|
+
INVALID_PARAMS = -32602
|
|
15
|
+
INTERNAL_ERROR = -32603
|
|
16
|
+
PARSE_ERROR = -32700
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
DEFAULT_ALLOWED_ID_CHARACTERS = /\A[a-zA-Z0-9_-]+\z/
|
|
20
|
+
|
|
21
|
+
extend self
|
|
22
|
+
|
|
23
|
+
def handle(request, id_validation_pattern: DEFAULT_ALLOWED_ID_CHARACTERS, &method_finder)
|
|
24
|
+
if request.is_a?(Array)
|
|
25
|
+
return error_response(id: :unknown_id, id_validation_pattern: id_validation_pattern, error: {
|
|
26
|
+
code: ErrorCode::INVALID_REQUEST,
|
|
27
|
+
message: "Invalid Request",
|
|
28
|
+
data: "Request is an empty array",
|
|
29
|
+
}) if request.empty?
|
|
30
|
+
|
|
31
|
+
# Handle batch requests
|
|
32
|
+
responses = request.map { |req| process_request(req, id_validation_pattern: id_validation_pattern, &method_finder) }.compact
|
|
33
|
+
|
|
34
|
+
# A single item is hoisted out of the array
|
|
35
|
+
return responses.first if responses.one?
|
|
36
|
+
|
|
37
|
+
# An empty array yields nil
|
|
38
|
+
responses if responses.any?
|
|
39
|
+
elsif request.is_a?(Hash)
|
|
40
|
+
# Handle single request
|
|
41
|
+
process_request(request, id_validation_pattern: id_validation_pattern, &method_finder)
|
|
42
|
+
else
|
|
43
|
+
error_response(id: :unknown_id, id_validation_pattern: id_validation_pattern, error: {
|
|
44
|
+
code: ErrorCode::INVALID_REQUEST,
|
|
45
|
+
message: "Invalid Request",
|
|
46
|
+
data: "Request must be an array or a hash",
|
|
47
|
+
})
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def handle_json(request_json, id_validation_pattern: DEFAULT_ALLOWED_ID_CHARACTERS, &method_finder)
|
|
52
|
+
begin
|
|
53
|
+
request = JSON.parse(request_json, symbolize_names: true)
|
|
54
|
+
response = handle(request, id_validation_pattern: id_validation_pattern, &method_finder)
|
|
55
|
+
rescue JSON::ParserError
|
|
56
|
+
response = error_response(id: :unknown_id, id_validation_pattern: id_validation_pattern, error: {
|
|
57
|
+
code: ErrorCode::PARSE_ERROR,
|
|
58
|
+
message: "Parse error",
|
|
59
|
+
data: "Invalid JSON",
|
|
60
|
+
})
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
response&.to_json
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def process_request(request, id_validation_pattern:, &method_finder)
|
|
67
|
+
id = request[:id]
|
|
68
|
+
|
|
69
|
+
error = if !valid_version?(request[:jsonrpc])
|
|
70
|
+
"JSON-RPC version must be 2.0"
|
|
71
|
+
elsif !valid_id?(request[:id], id_validation_pattern)
|
|
72
|
+
"Request ID must match validation pattern, or be an integer or null"
|
|
73
|
+
elsif !valid_method_name?(request[:method])
|
|
74
|
+
'Method name must be a string and not start with "rpc."'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
return error_response(id: :unknown_id, id_validation_pattern: id_validation_pattern, error: {
|
|
78
|
+
code: ErrorCode::INVALID_REQUEST,
|
|
79
|
+
message: "Invalid Request",
|
|
80
|
+
data: error,
|
|
81
|
+
}) if error
|
|
82
|
+
|
|
83
|
+
method_name = request[:method]
|
|
84
|
+
params = request[:params]
|
|
85
|
+
|
|
86
|
+
unless valid_params?(params)
|
|
87
|
+
return error_response(id: id, id_validation_pattern: id_validation_pattern, error: {
|
|
88
|
+
code: ErrorCode::INVALID_PARAMS,
|
|
89
|
+
message: "Invalid params",
|
|
90
|
+
data: "Method parameters must be an array or an object or null",
|
|
91
|
+
})
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
method = method_finder.call(method_name)
|
|
96
|
+
|
|
97
|
+
if method.nil?
|
|
98
|
+
return error_response(id: id, id_validation_pattern: id_validation_pattern, error: {
|
|
99
|
+
code: ErrorCode::METHOD_NOT_FOUND,
|
|
100
|
+
message: "Method not found",
|
|
101
|
+
data: method_name,
|
|
102
|
+
})
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
result = method.call(params)
|
|
106
|
+
|
|
107
|
+
success_response(id: id, result: result)
|
|
108
|
+
rescue StandardError => e
|
|
109
|
+
error_response(id: id, id_validation_pattern: id_validation_pattern, error: {
|
|
110
|
+
code: ErrorCode::INTERNAL_ERROR,
|
|
111
|
+
message: "Internal error",
|
|
112
|
+
data: e.message,
|
|
113
|
+
})
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def valid_version?(version)
|
|
118
|
+
version == Version::V2_0
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def valid_id?(id, pattern = nil)
|
|
122
|
+
return true if id.nil? || id.is_a?(Integer)
|
|
123
|
+
return false unless id.is_a?(String)
|
|
124
|
+
|
|
125
|
+
pattern ? id.match?(pattern) : true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def valid_method_name?(method)
|
|
129
|
+
method.is_a?(String) && !method.start_with?("rpc.")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def valid_params?(params)
|
|
133
|
+
params.nil? || params.is_a?(Array) || params.is_a?(Hash)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def success_response(id:, result:)
|
|
137
|
+
{
|
|
138
|
+
jsonrpc: Version::V2_0,
|
|
139
|
+
id: id,
|
|
140
|
+
result: result,
|
|
141
|
+
} unless id.nil?
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def error_response(id:, id_validation_pattern:, error:)
|
|
145
|
+
{
|
|
146
|
+
jsonrpc: Version::V2_0,
|
|
147
|
+
id: valid_id?(id, id_validation_pattern) ? id : nil,
|
|
148
|
+
error: error.compact,
|
|
149
|
+
} unless id.nil?
|
|
150
|
+
end
|
|
151
|
+
end
|
data/lib/mcp/client/http.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module MCP
|
|
4
4
|
class Client
|
|
5
5
|
class HTTP
|
|
6
|
+
ACCEPT_HEADER = "application/json, text/event-stream"
|
|
7
|
+
|
|
6
8
|
attr_reader :url
|
|
7
9
|
|
|
8
10
|
def initialize(url:, headers: {})
|
|
@@ -14,46 +16,48 @@ module MCP
|
|
|
14
16
|
method = request[:method] || request["method"]
|
|
15
17
|
params = request[:params] || request["params"]
|
|
16
18
|
|
|
17
|
-
client.post("", request)
|
|
19
|
+
response = client.post("", request)
|
|
20
|
+
validate_response_content_type!(response, method, params)
|
|
21
|
+
response.body
|
|
18
22
|
rescue Faraday::BadRequestError => e
|
|
19
23
|
raise RequestHandlerError.new(
|
|
20
24
|
"The #{method} request is invalid",
|
|
21
|
-
{ method
|
|
25
|
+
{ method: method, params: params },
|
|
22
26
|
error_type: :bad_request,
|
|
23
27
|
original_error: e,
|
|
24
28
|
)
|
|
25
29
|
rescue Faraday::UnauthorizedError => e
|
|
26
30
|
raise RequestHandlerError.new(
|
|
27
31
|
"You are unauthorized to make #{method} requests",
|
|
28
|
-
{ method
|
|
32
|
+
{ method: method, params: params },
|
|
29
33
|
error_type: :unauthorized,
|
|
30
34
|
original_error: e,
|
|
31
35
|
)
|
|
32
36
|
rescue Faraday::ForbiddenError => e
|
|
33
37
|
raise RequestHandlerError.new(
|
|
34
38
|
"You are forbidden to make #{method} requests",
|
|
35
|
-
{ method
|
|
39
|
+
{ method: method, params: params },
|
|
36
40
|
error_type: :forbidden,
|
|
37
41
|
original_error: e,
|
|
38
42
|
)
|
|
39
43
|
rescue Faraday::ResourceNotFound => e
|
|
40
44
|
raise RequestHandlerError.new(
|
|
41
45
|
"The #{method} request is not found",
|
|
42
|
-
{ method
|
|
46
|
+
{ method: method, params: params },
|
|
43
47
|
error_type: :not_found,
|
|
44
48
|
original_error: e,
|
|
45
49
|
)
|
|
46
50
|
rescue Faraday::UnprocessableEntityError => e
|
|
47
51
|
raise RequestHandlerError.new(
|
|
48
52
|
"The #{method} request is unprocessable",
|
|
49
|
-
{ method
|
|
53
|
+
{ method: method, params: params },
|
|
50
54
|
error_type: :unprocessable_entity,
|
|
51
55
|
original_error: e,
|
|
52
56
|
)
|
|
53
57
|
rescue Faraday::Error => e # Catch-all
|
|
54
58
|
raise RequestHandlerError.new(
|
|
55
59
|
"Internal error handling #{method} request",
|
|
56
|
-
{ method
|
|
60
|
+
{ method: method, params: params },
|
|
57
61
|
error_type: :internal_error,
|
|
58
62
|
original_error: e,
|
|
59
63
|
)
|
|
@@ -70,6 +74,7 @@ module MCP
|
|
|
70
74
|
faraday.response(:json)
|
|
71
75
|
faraday.response(:raise_error)
|
|
72
76
|
|
|
77
|
+
faraday.headers["Accept"] = ACCEPT_HEADER
|
|
73
78
|
headers.each do |key, value|
|
|
74
79
|
faraday.headers[key] = value
|
|
75
80
|
end
|
|
@@ -83,6 +88,17 @@ module MCP
|
|
|
83
88
|
"Add it to your Gemfile: gem 'faraday', '>= 2.0'" \
|
|
84
89
|
"See https://rubygems.org/gems/faraday for more details."
|
|
85
90
|
end
|
|
91
|
+
|
|
92
|
+
def validate_response_content_type!(response, method, params)
|
|
93
|
+
content_type = response.headers["Content-Type"]
|
|
94
|
+
return if content_type&.include?("application/json")
|
|
95
|
+
|
|
96
|
+
raise RequestHandlerError.new(
|
|
97
|
+
"Unsupported Content-Type: #{content_type.inspect}. This client only supports JSON responses.",
|
|
98
|
+
{ method: method, params: params },
|
|
99
|
+
error_type: :unsupported_media_type,
|
|
100
|
+
)
|
|
101
|
+
end
|
|
86
102
|
end
|
|
87
103
|
end
|
|
88
104
|
end
|
data/lib/mcp/client.rb
CHANGED
|
@@ -58,6 +58,20 @@ module MCP
|
|
|
58
58
|
response.dig("result", "resources") || []
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
# Returns the list of prompts available from the server.
|
|
62
|
+
# Each call will make a new request – the result is not cached.
|
|
63
|
+
#
|
|
64
|
+
# @return [Array<Hash>] An array of available prompts.
|
|
65
|
+
def prompts
|
|
66
|
+
response = transport.send_request(request: {
|
|
67
|
+
jsonrpc: JsonRpcHandler::Version::V2_0,
|
|
68
|
+
id: request_id,
|
|
69
|
+
method: "prompts/list",
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
response.dig("result", "prompts") || []
|
|
73
|
+
end
|
|
74
|
+
|
|
61
75
|
# Calls a tool via the transport layer and returns the full response from the server.
|
|
62
76
|
#
|
|
63
77
|
# @param tool [MCP::Client::Tool] The tool to be called.
|
|
@@ -96,6 +110,21 @@ module MCP
|
|
|
96
110
|
response.dig("result", "contents") || []
|
|
97
111
|
end
|
|
98
112
|
|
|
113
|
+
# Gets a prompt from the server by name and returns its details.
|
|
114
|
+
#
|
|
115
|
+
# @param name [String] The name of the prompt to get.
|
|
116
|
+
# @return [Hash] A hash containing the prompt details.
|
|
117
|
+
def get_prompt(name:)
|
|
118
|
+
response = transport.send_request(request: {
|
|
119
|
+
jsonrpc: JsonRpcHandler::Version::V2_0,
|
|
120
|
+
id: request_id,
|
|
121
|
+
method: "prompts/get",
|
|
122
|
+
params: { name: name },
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
response.fetch("result", {})
|
|
126
|
+
end
|
|
127
|
+
|
|
99
128
|
private
|
|
100
129
|
|
|
101
130
|
def request_id
|
data/lib/mcp/configuration.rb
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module MCP
|
|
4
4
|
class Configuration
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
LATEST_STABLE_PROTOCOL_VERSION = "2025-11-25"
|
|
6
|
+
SUPPORTED_STABLE_PROTOCOL_VERSIONS = [
|
|
7
|
+
LATEST_STABLE_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05",
|
|
8
|
+
]
|
|
7
9
|
|
|
8
10
|
attr_writer :exception_reporter, :instrumentation_callback
|
|
9
11
|
|
|
@@ -33,7 +35,7 @@ module MCP
|
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
def protocol_version
|
|
36
|
-
@protocol_version ||
|
|
38
|
+
@protocol_version || LATEST_STABLE_PROTOCOL_VERSION
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
def protocol_version?
|
|
@@ -83,18 +85,18 @@ module MCP
|
|
|
83
85
|
validate_tool_call_arguments = other.validate_tool_call_arguments
|
|
84
86
|
|
|
85
87
|
Configuration.new(
|
|
86
|
-
exception_reporter
|
|
87
|
-
instrumentation_callback
|
|
88
|
-
protocol_version
|
|
89
|
-
validate_tool_call_arguments
|
|
88
|
+
exception_reporter: exception_reporter,
|
|
89
|
+
instrumentation_callback: instrumentation_callback,
|
|
90
|
+
protocol_version: protocol_version,
|
|
91
|
+
validate_tool_call_arguments: validate_tool_call_arguments,
|
|
90
92
|
)
|
|
91
93
|
end
|
|
92
94
|
|
|
93
95
|
private
|
|
94
96
|
|
|
95
97
|
def validate_protocol_version!(protocol_version)
|
|
96
|
-
unless
|
|
97
|
-
message = "protocol_version must be #{
|
|
98
|
+
unless SUPPORTED_STABLE_PROTOCOL_VERSIONS.include?(protocol_version)
|
|
99
|
+
message = "protocol_version must be #{SUPPORTED_STABLE_PROTOCOL_VERSIONS[0...-1].join(", ")}, or #{SUPPORTED_STABLE_PROTOCOL_VERSIONS[-1]}"
|
|
98
100
|
raise ArgumentError, message
|
|
99
101
|
end
|
|
100
102
|
end
|
data/lib/mcp/content.rb
CHANGED
|
@@ -11,7 +11,7 @@ module MCP
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def to_h
|
|
14
|
-
{ text
|
|
14
|
+
{ text: text, annotations: annotations, type: "text" }.compact
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -25,7 +25,7 @@ module MCP
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def to_h
|
|
28
|
-
{ data
|
|
28
|
+
{ data: data, mime_type: mime_type, annotations: annotations, type: "image" }.compact
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
data/lib/mcp/icon.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MCP
|
|
4
|
+
class Icon
|
|
5
|
+
SUPPORTED_THEMES = ["light", "dark"]
|
|
6
|
+
|
|
7
|
+
attr_reader :mime_type, :sizes, :src, :theme
|
|
8
|
+
|
|
9
|
+
def initialize(mime_type: nil, sizes: nil, src: nil, theme: nil)
|
|
10
|
+
raise ArgumentError, 'The value of theme must specify "light" or "dark".' if theme && !SUPPORTED_THEMES.include?(theme)
|
|
11
|
+
|
|
12
|
+
@mime_type = mime_type
|
|
13
|
+
@sizes = sizes
|
|
14
|
+
@src = src
|
|
15
|
+
@theme = theme
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_h
|
|
19
|
+
{ mimeType: mime_type, sizes: sizes, src: src, theme: theme }.compact
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/mcp/instrumentation.rb
CHANGED
data/lib/mcp/methods.rb
CHANGED
|
@@ -21,6 +21,7 @@ module MCP
|
|
|
21
21
|
|
|
22
22
|
ROOTS_LIST = "roots/list"
|
|
23
23
|
SAMPLING_CREATE_MESSAGE = "sampling/createMessage"
|
|
24
|
+
ELICITATION_CREATE = "elicitation/create"
|
|
24
25
|
|
|
25
26
|
# Notification methods
|
|
26
27
|
NOTIFICATIONS_INITIALIZED = "notifications/initialized"
|
|
@@ -76,6 +77,8 @@ module MCP
|
|
|
76
77
|
require_capability!(method, capabilities, :roots, :listChanged)
|
|
77
78
|
when SAMPLING_CREATE_MESSAGE
|
|
78
79
|
require_capability!(method, capabilities, :sampling)
|
|
80
|
+
when ELICITATION_CREATE
|
|
81
|
+
require_capability!(method, capabilities, :elicitation)
|
|
79
82
|
when INITIALIZE, PING, NOTIFICATIONS_INITIALIZED, NOTIFICATIONS_PROGRESS, NOTIFICATIONS_CANCELLED
|
|
80
83
|
# No specific capability required for initialize, ping, progress or cancelled
|
|
81
84
|
end
|
data/lib/mcp/prompt/message.rb
CHANGED
data/lib/mcp/prompt/result.rb
CHANGED
data/lib/mcp/prompt.rb
CHANGED
|
@@ -7,6 +7,7 @@ module MCP
|
|
|
7
7
|
|
|
8
8
|
attr_reader :title_value
|
|
9
9
|
attr_reader :description_value
|
|
10
|
+
attr_reader :icons_value
|
|
10
11
|
attr_reader :arguments_value
|
|
11
12
|
attr_reader :meta_value
|
|
12
13
|
|
|
@@ -19,6 +20,7 @@ module MCP
|
|
|
19
20
|
name: name_value,
|
|
20
21
|
title: title_value,
|
|
21
22
|
description: description_value,
|
|
23
|
+
icons: icons&.map(&:to_h),
|
|
22
24
|
arguments: arguments_value&.map(&:to_h),
|
|
23
25
|
_meta: meta_value,
|
|
24
26
|
}.compact
|
|
@@ -29,6 +31,7 @@ module MCP
|
|
|
29
31
|
subclass.instance_variable_set(:@name_value, nil)
|
|
30
32
|
subclass.instance_variable_set(:@title_value, nil)
|
|
31
33
|
subclass.instance_variable_set(:@description_value, nil)
|
|
34
|
+
subclass.instance_variable_set(:@icons_value, nil)
|
|
32
35
|
subclass.instance_variable_set(:@arguments_value, nil)
|
|
33
36
|
subclass.instance_variable_set(:@meta_value, nil)
|
|
34
37
|
end
|
|
@@ -61,6 +64,14 @@ module MCP
|
|
|
61
64
|
end
|
|
62
65
|
end
|
|
63
66
|
|
|
67
|
+
def icons(value = NOT_SET)
|
|
68
|
+
if value == NOT_SET
|
|
69
|
+
@icons_value
|
|
70
|
+
else
|
|
71
|
+
@icons_value = value
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
64
75
|
def arguments(value = NOT_SET)
|
|
65
76
|
if value == NOT_SET
|
|
66
77
|
@arguments_value
|
|
@@ -77,14 +88,15 @@ module MCP
|
|
|
77
88
|
end
|
|
78
89
|
end
|
|
79
90
|
|
|
80
|
-
def define(name: nil, title: nil, description: nil, arguments: [], meta: nil, &block)
|
|
91
|
+
def define(name: nil, title: nil, description: nil, icons: [], arguments: [], meta: nil, &block)
|
|
81
92
|
Class.new(self) do
|
|
82
93
|
prompt_name name
|
|
83
94
|
title title
|
|
84
95
|
description description
|
|
96
|
+
icons icons
|
|
85
97
|
arguments arguments
|
|
86
98
|
define_singleton_method(:template) do |args, server_context: nil|
|
|
87
|
-
instance_exec(args, server_context
|
|
99
|
+
instance_exec(args, server_context: server_context, &block)
|
|
88
100
|
end
|
|
89
101
|
meta meta
|
|
90
102
|
end
|
data/lib/mcp/resource.rb
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module MCP
|
|
4
4
|
class Resource
|
|
5
|
-
attr_reader :uri, :name, :title, :description, :mime_type
|
|
5
|
+
attr_reader :uri, :name, :title, :description, :icons, :mime_type
|
|
6
6
|
|
|
7
|
-
def initialize(uri:, name:, title: nil, description: nil, mime_type: nil)
|
|
7
|
+
def initialize(uri:, name:, title: nil, description: nil, icons: [], mime_type: nil)
|
|
8
8
|
@uri = uri
|
|
9
9
|
@name = name
|
|
10
10
|
@title = title
|
|
11
11
|
@description = description
|
|
12
|
+
@icons = icons
|
|
12
13
|
@mime_type = mime_type
|
|
13
14
|
end
|
|
14
15
|
|
|
@@ -18,6 +19,7 @@ module MCP
|
|
|
18
19
|
name: name,
|
|
19
20
|
title: title,
|
|
20
21
|
description: description,
|
|
22
|
+
icons: icons.map(&:to_h),
|
|
21
23
|
mimeType: mime_type,
|
|
22
24
|
}.compact
|
|
23
25
|
end
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module MCP
|
|
4
4
|
class ResourceTemplate
|
|
5
|
-
attr_reader :uri_template, :name, :title, :description, :mime_type
|
|
5
|
+
attr_reader :uri_template, :name, :title, :description, :icons, :mime_type
|
|
6
6
|
|
|
7
|
-
def initialize(uri_template:, name:, title: nil, description: nil, mime_type: nil)
|
|
7
|
+
def initialize(uri_template:, name:, title: nil, description: nil, icons: [], mime_type: nil)
|
|
8
8
|
@uri_template = uri_template
|
|
9
9
|
@name = name
|
|
10
10
|
@title = title
|
|
11
11
|
@description = description
|
|
12
|
+
@icons = icons
|
|
12
13
|
@mime_type = mime_type
|
|
13
14
|
end
|
|
14
15
|
|
|
@@ -18,6 +19,7 @@ module MCP
|
|
|
18
19
|
name: name,
|
|
19
20
|
title: title,
|
|
20
21
|
description: description,
|
|
22
|
+
icons: icons.map(&:to_h),
|
|
21
23
|
mimeType: mime_type,
|
|
22
24
|
}.compact
|
|
23
25
|
end
|