mcp 0.17.0 → 0.18.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 +20 -1
- data/lib/mcp/client/stdio.rb +4 -1
- data/lib/mcp/server/transports/streamable_http_transport.rb +2 -2
- data/lib/mcp/server.rb +5 -0
- data/lib/mcp/server_context.rb +22 -0
- data/lib/mcp/server_session.rb +8 -0
- data/lib/mcp/tool/schema.rb +42 -1
- data/lib/mcp/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7268234a54e4c0b422a916aabc08de04a4cde157b9a6b3909274ab4629885137
|
|
4
|
+
data.tar.gz: 1f48ca777ef0269c8c1f946a23efed1cd990e9ac1898568d8143d84e6c8d7fcb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb859fc7e68f54f1a1d7c3d523a3487799c7b5ff8e3ddb5c01cd84101be79c9b5f81eeeb65b8fb06d620b87898f6c3466492e57ad921eac729c113cd5a609f8d
|
|
7
|
+
data.tar.gz: 0bf7b67aa29e8211c97168a9e9472e5fdfb1ea5a97db16d917ab1602f584db6076854e354abe0456384c70e712a797832c0949f12d5a5e76bea6761fcecab0c1
|
data/README.md
CHANGED
|
@@ -1253,6 +1253,25 @@ A `ping` request has no parameters, and the receiver MUST respond promptly with
|
|
|
1253
1253
|
Servers respond to incoming `ping` requests automatically - no setup is required.
|
|
1254
1254
|
Any `MCP::Server` instance replies with an empty result.
|
|
1255
1255
|
|
|
1256
|
+
Servers can also send `ping` requests to the client via `ServerSession#ping`.
|
|
1257
|
+
Inside a tool handler that receives `server_context:`, call `ping` on it:
|
|
1258
|
+
|
|
1259
|
+
```ruby
|
|
1260
|
+
class HealthCheckTool < MCP::Tool
|
|
1261
|
+
description "Verifies the client is still responsive"
|
|
1262
|
+
|
|
1263
|
+
def self.call(server_context:)
|
|
1264
|
+
server_context.ping # => {} on success
|
|
1265
|
+
|
|
1266
|
+
MCP::Tool::Response.new([{ type: "text", text: "client is alive" }])
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
`#ping` raises `MCP::Server::ValidationError` when the client returns a `result`
|
|
1272
|
+
that is not a Hash. Transport-level errors (e.g., the client returning a JSON-RPC error)
|
|
1273
|
+
propagate as exceptions raised by the transport layer.
|
|
1274
|
+
|
|
1256
1275
|
#### Client-Side
|
|
1257
1276
|
|
|
1258
1277
|
`MCP::Client` exposes `ping` to send a ping to the server:
|
|
@@ -1736,7 +1755,7 @@ This class supports:
|
|
|
1736
1755
|
|
|
1737
1756
|
- Liveness check via the `ping` method (`MCP::Client#ping`)
|
|
1738
1757
|
- Tool listing via the `tools/list` method (`MCP::Client#tools`)
|
|
1739
|
-
- Tool invocation via the `tools/call` method (`MCP::Client#
|
|
1758
|
+
- Tool invocation via the `tools/call` method (`MCP::Client#call_tool`)
|
|
1740
1759
|
- Resource listing via the `resources/list` method (`MCP::Client#resources`)
|
|
1741
1760
|
- Resource template listing via the `resources/templates/list` method (`MCP::Client#resource_templates`)
|
|
1742
1761
|
- Resource reading via the `resources/read` method (`MCP::Client#read_resource`)
|
data/lib/mcp/client/stdio.rb
CHANGED
|
@@ -134,7 +134,10 @@ module MCP
|
|
|
134
134
|
|
|
135
135
|
def send_request(request:)
|
|
136
136
|
start unless @started
|
|
137
|
-
|
|
137
|
+
unless @initialized
|
|
138
|
+
warn("Calling `MCP::Client::Stdio#send_request` without calling `MCP::Client#connect` is deprecated. Use `MCP::Client#connect` before sending requests instead.", uplevel: 1)
|
|
139
|
+
connect
|
|
140
|
+
end
|
|
138
141
|
|
|
139
142
|
write_message(request)
|
|
140
143
|
read_response(request)
|
|
@@ -426,7 +426,7 @@ module MCP
|
|
|
426
426
|
return success_response
|
|
427
427
|
end
|
|
428
428
|
|
|
429
|
-
return missing_session_id_response unless (session_id = request
|
|
429
|
+
return missing_session_id_response unless (session_id = extract_session_id(request))
|
|
430
430
|
return session_not_found_response unless session_exists?(session_id)
|
|
431
431
|
|
|
432
432
|
protocol_version_error = validate_protocol_version_header(request)
|
|
@@ -504,7 +504,7 @@ module MCP
|
|
|
504
504
|
|
|
505
505
|
def parse_accept_header(header)
|
|
506
506
|
header.split(",").map do |part|
|
|
507
|
-
part.split(";").first.strip
|
|
507
|
+
part.split(";").first.strip.downcase
|
|
508
508
|
end
|
|
509
509
|
end
|
|
510
510
|
|
data/lib/mcp/server.rb
CHANGED
|
@@ -67,6 +67,11 @@ module MCP
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# Raised when a client response fails server-side validation, e.g., a success response
|
|
71
|
+
# whose `result` field is missing or has the wrong type. This is distinct from a
|
|
72
|
+
# client-returned JSON-RPC error.
|
|
73
|
+
class ValidationError < StandardError; end
|
|
74
|
+
|
|
70
75
|
include Instrumentation
|
|
71
76
|
include Pagination
|
|
72
77
|
|
data/lib/mcp/server_context.rb
CHANGED
|
@@ -59,6 +59,28 @@ module MCP
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# Sends a `ping` request to the originating client to verify it is still responsive.
|
|
63
|
+
# Per the MCP spec, the client MUST respond promptly with an empty result.
|
|
64
|
+
#
|
|
65
|
+
# @return [Hash] An empty hash on success.
|
|
66
|
+
# @raise [Server::ValidationError] If the response `result` is not a Hash.
|
|
67
|
+
# @raise [NoMethodError] If the session does not support sending pings.
|
|
68
|
+
#
|
|
69
|
+
# @example
|
|
70
|
+
# def self.call(server_context:)
|
|
71
|
+
# server_context.ping # => {}
|
|
72
|
+
# # ...
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# @see https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/ping
|
|
76
|
+
def ping
|
|
77
|
+
if @notification_target.respond_to?(:ping)
|
|
78
|
+
@notification_target.ping(related_request_id: @related_request_id)
|
|
79
|
+
else
|
|
80
|
+
raise NoMethodError, "undefined method 'ping' for #{self}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
62
84
|
# Delegates to the session so the request is scoped to the originating client.
|
|
63
85
|
# Falls back to `@context` (via `method_missing`) when `@notification_target`
|
|
64
86
|
# does not support sampling.
|
data/lib/mcp/server_session.rb
CHANGED
|
@@ -106,6 +106,14 @@ module MCP
|
|
|
106
106
|
send_to_transport_request(Methods::ROOTS_LIST, nil, related_request_id: related_request_id)
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Sends a `ping` request scoped to this session.
|
|
110
|
+
def ping(related_request_id: nil)
|
|
111
|
+
result = send_to_transport_request(Methods::PING, nil, related_request_id: related_request_id)
|
|
112
|
+
raise Server::ValidationError, "Response validation failed: invalid `result`" unless result.is_a?(Hash)
|
|
113
|
+
|
|
114
|
+
result
|
|
115
|
+
end
|
|
116
|
+
|
|
109
117
|
# Sends a `sampling/createMessage` request scoped to this session.
|
|
110
118
|
def create_sampling_message(related_request_id: nil, **kwargs)
|
|
111
119
|
params = @server.build_sampling_params(client_capabilities, **kwargs)
|
data/lib/mcp/tool/schema.rb
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "digest"
|
|
3
4
|
require "json-schema"
|
|
4
5
|
|
|
5
6
|
module MCP
|
|
6
7
|
class Tool
|
|
7
8
|
class Schema
|
|
9
|
+
# Metaschema validation depends only on schema content, so a given schema
|
|
10
|
+
# never needs to be validated more than once. Caching the result lets repeated
|
|
11
|
+
# (e.g. dynamically rebuilt) schemas skip the costly traversal.
|
|
12
|
+
class ValidationCache
|
|
13
|
+
DEFAULT_MAX_SIZE = 1000
|
|
14
|
+
|
|
15
|
+
def initialize(max_size: DEFAULT_MAX_SIZE)
|
|
16
|
+
@max_size = max_size
|
|
17
|
+
@entries = {}
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def validated?(key)
|
|
22
|
+
@mutex.synchronize { @entries.key?(key) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def store(key)
|
|
26
|
+
@mutex.synchronize do
|
|
27
|
+
@entries.delete(key)
|
|
28
|
+
@entries[key] = true
|
|
29
|
+
@entries.shift while @entries.size > @max_size
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def clear
|
|
34
|
+
@mutex.synchronize { @entries.clear }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
VALIDATION_CACHE = ValidationCache.new
|
|
38
|
+
|
|
8
39
|
# JSON Schema 2020-12 is the default dialect for MCP schema definitions
|
|
9
40
|
# per MCP 2025-11-25 (SEP-1613). Note: emission only — runtime validation
|
|
10
41
|
# is still performed against the JSON Schema draft-04 metaschema because
|
|
@@ -36,6 +67,14 @@ module MCP
|
|
|
36
67
|
end
|
|
37
68
|
|
|
38
69
|
def validate_schema!
|
|
70
|
+
target = schema_for_validation
|
|
71
|
+
|
|
72
|
+
# `max_nesting: false` because normalization uses `JSON.dump` (no nesting limit),
|
|
73
|
+
# so the default `JSON.generate` limit would raise on a deeply nested schema that
|
|
74
|
+
# the initializer already accepted.
|
|
75
|
+
key = Digest::SHA256.hexdigest(JSON.generate(target, max_nesting: false))
|
|
76
|
+
return if VALIDATION_CACHE.validated?(key)
|
|
77
|
+
|
|
39
78
|
gem_path = File.realpath(Gem.loaded_specs["json-schema"].full_gem_path)
|
|
40
79
|
schema_reader = JSON::Schema::Reader.new(
|
|
41
80
|
accept_uri: false,
|
|
@@ -45,10 +84,12 @@ module MCP
|
|
|
45
84
|
# Converts metaschema to a file URI for cross-platform compatibility
|
|
46
85
|
metaschema_uri = JSON::Util::URI.file_uri(metaschema_path.expand_path.cleanpath.to_s.tr("\\", "/"))
|
|
47
86
|
metaschema = metaschema_uri.to_s
|
|
48
|
-
errors = JSON::Validator.fully_validate(metaschema,
|
|
87
|
+
errors = JSON::Validator.fully_validate(metaschema, target, schema_reader: schema_reader)
|
|
49
88
|
if errors.any?
|
|
50
89
|
raise ArgumentError, "Invalid JSON Schema: #{errors.join(", ")}"
|
|
51
90
|
end
|
|
91
|
+
|
|
92
|
+
VALIDATION_CACHE.store(key)
|
|
52
93
|
end
|
|
53
94
|
|
|
54
95
|
# The `json-schema` gem's draft-04 validator cannot resolve newer or unknown `$schema`
|
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.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Model Context Protocol
|
|
@@ -87,7 +87,7 @@ licenses:
|
|
|
87
87
|
- Apache-2.0
|
|
88
88
|
metadata:
|
|
89
89
|
allowed_push_host: https://rubygems.org
|
|
90
|
-
changelog_uri: https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.
|
|
90
|
+
changelog_uri: https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.18.0
|
|
91
91
|
homepage_uri: https://ruby.sdk.modelcontextprotocol.io
|
|
92
92
|
source_code_uri: https://github.com/modelcontextprotocol/ruby-sdk
|
|
93
93
|
bug_tracker_uri: https://github.com/modelcontextprotocol/ruby-sdk/issues
|