mcp 0.11.0 → 0.13.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 +997 -885
- data/lib/json_rpc_handler.rb +16 -9
- data/lib/mcp/client/http.rb +4 -1
- data/lib/mcp/configuration.rb +38 -2
- data/lib/mcp/content.rb +16 -12
- data/lib/mcp/instrumentation.rb +23 -2
- data/lib/mcp/methods.rb +3 -2
- data/lib/mcp/prompt/result.rb +4 -3
- data/lib/mcp/resource/contents.rb +8 -7
- data/lib/mcp/resource.rb +4 -2
- data/lib/mcp/resource_template.rb +4 -2
- data/lib/mcp/server/transports/streamable_http_transport.rb +46 -17
- data/lib/mcp/server.rb +27 -45
- data/lib/mcp/server_context.rb +36 -0
- data/lib/mcp/server_session.rb +29 -0
- data/lib/mcp/tool/response.rb +4 -3
- data/lib/mcp/transport.rb +1 -0
- data/lib/mcp/version.rb +1 -1
- metadata +2 -2
data/lib/mcp/server.rb
CHANGED
|
@@ -31,14 +31,27 @@ module MCP
|
|
|
31
31
|
MAX_COMPLETION_VALUES = 100
|
|
32
32
|
|
|
33
33
|
class RequestHandlerError < StandardError
|
|
34
|
-
attr_reader :error_type
|
|
35
|
-
attr_reader :original_error
|
|
34
|
+
attr_reader :error_type, :original_error, :error_code, :error_data
|
|
36
35
|
|
|
37
|
-
def initialize(message, request, error_type: :internal_error, original_error: nil)
|
|
36
|
+
def initialize(message, request, error_type: :internal_error, original_error: nil, error_code: nil, error_data: nil)
|
|
38
37
|
super(message)
|
|
39
38
|
@request = request
|
|
40
39
|
@error_type = error_type
|
|
41
40
|
@original_error = original_error
|
|
41
|
+
@error_code = error_code
|
|
42
|
+
@error_data = error_data
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class URLElicitationRequiredError < RequestHandlerError
|
|
47
|
+
def initialize(elicitations)
|
|
48
|
+
super(
|
|
49
|
+
"URL elicitation required",
|
|
50
|
+
nil,
|
|
51
|
+
error_type: :url_elicitation_required,
|
|
52
|
+
error_code: -32042,
|
|
53
|
+
error_data: { elicitations: elicitations },
|
|
54
|
+
)
|
|
42
55
|
end
|
|
43
56
|
end
|
|
44
57
|
|
|
@@ -114,7 +127,6 @@ module MCP
|
|
|
114
127
|
# No op handlers for currently unsupported methods
|
|
115
128
|
Methods::RESOURCES_SUBSCRIBE => ->(_) { {} },
|
|
116
129
|
Methods::RESOURCES_UNSUBSCRIBE => ->(_) { {} },
|
|
117
|
-
Methods::ELICITATION_CREATE => ->(_) {},
|
|
118
130
|
}
|
|
119
131
|
@transport = transport
|
|
120
132
|
end
|
|
@@ -206,44 +218,6 @@ module MCP
|
|
|
206
218
|
report_exception(e, { notification: "log_message" })
|
|
207
219
|
end
|
|
208
220
|
|
|
209
|
-
# Sends a `sampling/createMessage` request to the client.
|
|
210
|
-
# For single-client transports (e.g., `StdioTransport`). For multi-client transports
|
|
211
|
-
# (e.g., `StreamableHTTPTransport`), use `ServerSession#create_sampling_message` instead
|
|
212
|
-
# to ensure the request is routed to the correct client.
|
|
213
|
-
def create_sampling_message(
|
|
214
|
-
messages:,
|
|
215
|
-
max_tokens:,
|
|
216
|
-
system_prompt: nil,
|
|
217
|
-
model_preferences: nil,
|
|
218
|
-
include_context: nil,
|
|
219
|
-
temperature: nil,
|
|
220
|
-
stop_sequences: nil,
|
|
221
|
-
metadata: nil,
|
|
222
|
-
tools: nil,
|
|
223
|
-
tool_choice: nil,
|
|
224
|
-
related_request_id: nil
|
|
225
|
-
)
|
|
226
|
-
unless @transport
|
|
227
|
-
raise "Cannot send sampling request without a transport."
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
params = build_sampling_params(
|
|
231
|
-
@client_capabilities,
|
|
232
|
-
messages: messages,
|
|
233
|
-
max_tokens: max_tokens,
|
|
234
|
-
system_prompt: system_prompt,
|
|
235
|
-
model_preferences: model_preferences,
|
|
236
|
-
include_context: include_context,
|
|
237
|
-
temperature: temperature,
|
|
238
|
-
stop_sequences: stop_sequences,
|
|
239
|
-
metadata: metadata,
|
|
240
|
-
tools: tools,
|
|
241
|
-
tool_choice: tool_choice,
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
@transport.send_request(Methods::SAMPLING_CREATE_MESSAGE, params)
|
|
245
|
-
end
|
|
246
|
-
|
|
247
221
|
# Sets a custom handler for `resources/read` requests.
|
|
248
222
|
# The block receives the parsed request params and should return resource
|
|
249
223
|
# contents. The return value is set as the `contents` field of the response.
|
|
@@ -375,7 +349,7 @@ module MCP
|
|
|
375
349
|
def handle_request(request, method, session: nil, related_request_id: nil)
|
|
376
350
|
handler = @handlers[method]
|
|
377
351
|
unless handler
|
|
378
|
-
instrument_call("unsupported_method") do
|
|
352
|
+
instrument_call("unsupported_method", server_context: { request: request }) do
|
|
379
353
|
client = session&.client || @client
|
|
380
354
|
add_instrumentation_data(client: client) if client
|
|
381
355
|
end
|
|
@@ -385,7 +359,12 @@ module MCP
|
|
|
385
359
|
Methods.ensure_capability!(method, capabilities)
|
|
386
360
|
|
|
387
361
|
->(params) {
|
|
388
|
-
|
|
362
|
+
reported_exception = nil
|
|
363
|
+
instrument_call(
|
|
364
|
+
method,
|
|
365
|
+
server_context: { request: request },
|
|
366
|
+
exception_already_reported: ->(e) { reported_exception.equal?(e) },
|
|
367
|
+
) do
|
|
389
368
|
result = case method
|
|
390
369
|
when Methods::INITIALIZE
|
|
391
370
|
init(params, session: session)
|
|
@@ -415,11 +394,14 @@ module MCP
|
|
|
415
394
|
rescue RequestHandlerError => e
|
|
416
395
|
report_exception(e.original_error || e, { request: request })
|
|
417
396
|
add_instrumentation_data(error: e.error_type)
|
|
397
|
+
reported_exception = e
|
|
418
398
|
raise e
|
|
419
399
|
rescue => e
|
|
420
400
|
report_exception(e, { request: request })
|
|
421
401
|
add_instrumentation_data(error: :internal_error)
|
|
422
|
-
|
|
402
|
+
wrapped = RequestHandlerError.new("Internal error handling #{method} request", request, original_error: e)
|
|
403
|
+
reported_exception = wrapped
|
|
404
|
+
raise wrapped
|
|
423
405
|
end
|
|
424
406
|
}
|
|
425
407
|
end
|
data/lib/mcp/server_context.rb
CHANGED
|
@@ -43,6 +43,42 @@ module MCP
|
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Delegates to the session so the request is scoped to the originating client.
|
|
47
|
+
# Falls back to `@context` (via `method_missing`) when `@notification_target`
|
|
48
|
+
# does not support elicitation.
|
|
49
|
+
def create_form_elicitation(**kwargs)
|
|
50
|
+
if @notification_target.respond_to?(:create_form_elicitation)
|
|
51
|
+
@notification_target.create_form_elicitation(**kwargs, related_request_id: @related_request_id)
|
|
52
|
+
elsif @context.respond_to?(:create_form_elicitation)
|
|
53
|
+
@context.create_form_elicitation(**kwargs, related_request_id: @related_request_id)
|
|
54
|
+
else
|
|
55
|
+
raise NoMethodError, "undefined method 'create_form_elicitation' for #{self}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Delegates to the session so the request is scoped to the originating client.
|
|
60
|
+
# Falls back to `@context` when `@notification_target` does not support URL mode elicitation.
|
|
61
|
+
def create_url_elicitation(**kwargs)
|
|
62
|
+
if @notification_target.respond_to?(:create_url_elicitation)
|
|
63
|
+
@notification_target.create_url_elicitation(**kwargs, related_request_id: @related_request_id)
|
|
64
|
+
elsif @context.respond_to?(:create_url_elicitation)
|
|
65
|
+
@context.create_url_elicitation(**kwargs, related_request_id: @related_request_id)
|
|
66
|
+
else
|
|
67
|
+
raise NoMethodError, "undefined method 'create_url_elicitation' for #{self}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Delegates to the session so the notification is scoped to the originating client.
|
|
72
|
+
def notify_elicitation_complete(**kwargs)
|
|
73
|
+
if @notification_target.respond_to?(:notify_elicitation_complete)
|
|
74
|
+
@notification_target.notify_elicitation_complete(**kwargs)
|
|
75
|
+
elsif @context.respond_to?(:notify_elicitation_complete)
|
|
76
|
+
@context.notify_elicitation_complete(**kwargs)
|
|
77
|
+
else
|
|
78
|
+
raise NoMethodError, "undefined method 'notify_elicitation_complete' for #{self}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
46
82
|
def method_missing(name, ...)
|
|
47
83
|
if @context.respond_to?(name)
|
|
48
84
|
@context.public_send(name, ...)
|
data/lib/mcp/server_session.rb
CHANGED
|
@@ -47,6 +47,35 @@ module MCP
|
|
|
47
47
|
send_to_transport_request(Methods::SAMPLING_CREATE_MESSAGE, params, related_request_id: related_request_id)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# Sends an `elicitation/create` request (form mode) scoped to this session.
|
|
51
|
+
def create_form_elicitation(message:, requested_schema:, related_request_id: nil)
|
|
52
|
+
unless client_capabilities&.dig(:elicitation)
|
|
53
|
+
raise "Client does not support elicitation. " \
|
|
54
|
+
"The client must declare the `elicitation` capability during initialization."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
params = { mode: "form", message: message, requestedSchema: requested_schema }
|
|
58
|
+
send_to_transport_request(Methods::ELICITATION_CREATE, params, related_request_id: related_request_id)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Sends an `elicitation/create` request (URL mode) scoped to this session.
|
|
62
|
+
def create_url_elicitation(message:, url:, elicitation_id:, related_request_id: nil)
|
|
63
|
+
unless client_capabilities&.dig(:elicitation, :url)
|
|
64
|
+
raise "Client does not support URL mode elicitation. " \
|
|
65
|
+
"The client must declare the `elicitation.url` capability during initialization."
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
params = { mode: "url", message: message, url: url, elicitationId: elicitation_id }
|
|
69
|
+
send_to_transport_request(Methods::ELICITATION_CREATE, params, related_request_id: related_request_id)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Sends an elicitation complete notification scoped to this session.
|
|
73
|
+
def notify_elicitation_complete(elicitation_id:)
|
|
74
|
+
send_to_transport(Methods::NOTIFICATIONS_ELICITATION_COMPLETE, { elicitationId: elicitation_id })
|
|
75
|
+
rescue => e
|
|
76
|
+
@server.report_exception(e, notification: "elicitation_complete")
|
|
77
|
+
end
|
|
78
|
+
|
|
50
79
|
# Sends a progress notification to this session only.
|
|
51
80
|
def notify_progress(progress_token:, progress:, total: nil, message: nil, related_request_id: nil)
|
|
52
81
|
params = {
|
data/lib/mcp/tool/response.rb
CHANGED
|
@@ -5,9 +5,9 @@ module MCP
|
|
|
5
5
|
class Response
|
|
6
6
|
NOT_GIVEN = Object.new.freeze
|
|
7
7
|
|
|
8
|
-
attr_reader :content, :structured_content
|
|
8
|
+
attr_reader :content, :structured_content, :meta
|
|
9
9
|
|
|
10
|
-
def initialize(content = nil, deprecated_error = NOT_GIVEN, error: false, structured_content: nil)
|
|
10
|
+
def initialize(content = nil, deprecated_error = NOT_GIVEN, error: false, structured_content: nil, meta: nil)
|
|
11
11
|
if deprecated_error != NOT_GIVEN
|
|
12
12
|
warn("Passing `error` with the 2nd argument of `Response.new` is deprecated. Use keyword argument like `Response.new(content, error: error)` instead.", uplevel: 1)
|
|
13
13
|
error = deprecated_error
|
|
@@ -16,6 +16,7 @@ module MCP
|
|
|
16
16
|
@content = content || []
|
|
17
17
|
@error = error
|
|
18
18
|
@structured_content = structured_content
|
|
19
|
+
@meta = meta
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def error?
|
|
@@ -23,7 +24,7 @@ module MCP
|
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def to_h
|
|
26
|
-
{ content: content, isError: error?, structuredContent: @structured_content }.compact
|
|
27
|
+
{ content: content, isError: error?, structuredContent: @structured_content, _meta: meta }.compact
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
end
|
data/lib/mcp/transport.rb
CHANGED
data/lib/mcp/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mcp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Model Context Protocol
|
|
@@ -78,7 +78,7 @@ licenses:
|
|
|
78
78
|
- Apache-2.0
|
|
79
79
|
metadata:
|
|
80
80
|
allowed_push_host: https://rubygems.org
|
|
81
|
-
changelog_uri: https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.
|
|
81
|
+
changelog_uri: https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.13.0
|
|
82
82
|
homepage_uri: https://github.com/modelcontextprotocol/ruby-sdk
|
|
83
83
|
source_code_uri: https://github.com/modelcontextprotocol/ruby-sdk
|
|
84
84
|
bug_tracker_uri: https://github.com/modelcontextprotocol/ruby-sdk/issues
|