model-context-protocol-rb 0.4.0 → 0.5.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/CHANGELOG.md +14 -1
- data/README.md +337 -158
- data/lib/model_context_protocol/server/cancellable.rb +54 -0
- data/lib/model_context_protocol/server/configuration.rb +4 -9
- data/lib/model_context_protocol/server/progressable.rb +72 -0
- data/lib/model_context_protocol/server/prompt.rb +3 -1
- data/lib/model_context_protocol/server/redis_client_proxy.rb +134 -0
- data/lib/model_context_protocol/server/redis_config.rb +108 -0
- data/lib/model_context_protocol/server/redis_pool_manager.rb +110 -0
- data/lib/model_context_protocol/server/resource.rb +3 -0
- data/lib/model_context_protocol/server/router.rb +36 -3
- data/lib/model_context_protocol/server/stdio_transport/request_store.rb +102 -0
- data/lib/model_context_protocol/server/stdio_transport.rb +31 -6
- data/lib/model_context_protocol/server/streamable_http_transport/event_counter.rb +35 -0
- data/lib/model_context_protocol/server/streamable_http_transport/message_poller.rb +101 -0
- data/lib/model_context_protocol/server/streamable_http_transport/notification_queue.rb +80 -0
- data/lib/model_context_protocol/server/streamable_http_transport/request_store.rb +224 -0
- data/lib/model_context_protocol/server/streamable_http_transport/session_message_queue.rb +120 -0
- data/lib/model_context_protocol/server/{session_store.rb → streamable_http_transport/session_store.rb} +30 -16
- data/lib/model_context_protocol/server/streamable_http_transport/stream_registry.rb +119 -0
- data/lib/model_context_protocol/server/streamable_http_transport.rb +181 -80
- data/lib/model_context_protocol/server/tool.rb +4 -0
- data/lib/model_context_protocol/server.rb +9 -3
- data/lib/model_context_protocol/version.rb +1 -1
- data/tasks/templates/dev-http.erb +58 -14
- metadata +57 -3
@@ -4,6 +4,8 @@ require "bundler/setup"
|
|
4
4
|
require "rack"
|
5
5
|
require 'rackup/handler/webrick'
|
6
6
|
require "webrick"
|
7
|
+
require "webrick/https"
|
8
|
+
require "openssl"
|
7
9
|
require "securerandom"
|
8
10
|
require "redis"
|
9
11
|
require "logger"
|
@@ -12,6 +14,14 @@ require 'stringio'
|
|
12
14
|
|
13
15
|
require_relative "../lib/model_context_protocol"
|
14
16
|
|
17
|
+
ModelContextProtocol::Server.configure_redis do |config|
|
18
|
+
config.redis_url = "redis://localhost:6379/0"
|
19
|
+
config.pool_size = 10
|
20
|
+
config.enable_reaper = true
|
21
|
+
config.reaper_interval = 10
|
22
|
+
config.idle_timeout = 15
|
23
|
+
end
|
24
|
+
|
15
25
|
Dir[File.join(__dir__, "../spec/support/**/*.rb")].each { |file| require file }
|
16
26
|
|
17
27
|
logger = Logger.new(STDOUT)
|
@@ -73,6 +83,11 @@ class MCPHttpApp
|
|
73
83
|
@logger.info(" Request: #{body_content}") unless body_content.empty?
|
74
84
|
end
|
75
85
|
|
86
|
+
if ModelContextProtocol::Server::RedisConfig.configured?
|
87
|
+
pool_stats = ModelContextProtocol::Server::RedisConfig.stats
|
88
|
+
@logger.info(" Redis Pool: #{pool_stats}")
|
89
|
+
end
|
90
|
+
|
76
91
|
env['rack.input'] = StringIO.new(body_content)
|
77
92
|
request = Rack::Request.new(env)
|
78
93
|
|
@@ -89,30 +104,26 @@ class MCPHttpApp
|
|
89
104
|
}, [""]]
|
90
105
|
end
|
91
106
|
|
92
|
-
begin
|
93
|
-
redis_client = Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"))
|
94
|
-
@logger.debug("Testing Redis connection...")
|
95
|
-
redis_client.ping
|
96
|
-
@logger.info("Redis connected successfully")
|
97
|
-
end
|
98
|
-
|
99
107
|
transport_config = {
|
100
108
|
type: :streamable_http,
|
101
|
-
env
|
102
|
-
|
103
|
-
require_sessions: false, # Optional sessions for easier testing
|
109
|
+
env:,
|
110
|
+
require_sessions: false,
|
104
111
|
session_ttl: 3600,
|
105
|
-
validate_origin: false, # Disable for testing
|
106
112
|
allowed_origins: ["*"]
|
107
113
|
}
|
108
114
|
|
109
115
|
@logger.debug("Creating MCP server with transport config")
|
110
116
|
server = create_mcp_server(transport_config)
|
117
|
+
transport = nil
|
111
118
|
|
112
119
|
begin
|
113
120
|
@logger.debug("Starting MCP server")
|
114
121
|
result = server.start
|
115
122
|
|
123
|
+
if server.respond_to?(:transport)
|
124
|
+
transport = server.transport
|
125
|
+
end
|
126
|
+
|
116
127
|
case result
|
117
128
|
when Hash
|
118
129
|
if result[:stream]
|
@@ -134,6 +145,10 @@ class MCPHttpApp
|
|
134
145
|
@logger.info("← ERROR RESPONSE (code: #{response_json[:error][:code]})")
|
135
146
|
elsif status == 202
|
136
147
|
@logger.info("← NOTIFICATION ACCEPTED [NO RESPONSE REQUIRED]")
|
148
|
+
elsif response_json[:accepted] == true && status == 200
|
149
|
+
method = request_json['method'] rescue 'unknown'
|
150
|
+
id = request_json['id'] rescue 'unknown'
|
151
|
+
@logger.info("← #{method} RESPONSE (id: #{id}) [DELIVERED VIA SSE STREAM]")
|
137
152
|
elsif response_json[:result]
|
138
153
|
method = request_json['method'] rescue 'unknown'
|
139
154
|
@logger.info("← #{method} RESPONSE (id: #{response_json[:id]})")
|
@@ -167,6 +182,7 @@ class MCPHttpApp
|
|
167
182
|
@logger.debug("Full backtrace:\n#{e.backtrace.join("\n")}")
|
168
183
|
[500, {"Content-Type" => "application/json"}, [%Q({"error": "Internal server error: #{e.message}"})]]
|
169
184
|
ensure
|
185
|
+
transport&.cleanup if transport&.respond_to?(:cleanup)
|
170
186
|
Thread.current[:request_id] = nil
|
171
187
|
end
|
172
188
|
end
|
@@ -217,13 +233,18 @@ class MCPHttpApp
|
|
217
233
|
register TestToolWithMixedContentResponse
|
218
234
|
register TestToolWithResourceResponse
|
219
235
|
register TestToolWithToolErrorResponse
|
236
|
+
register TestToolWithCancellableSleep
|
220
237
|
end
|
221
238
|
end
|
222
239
|
end
|
223
240
|
end
|
224
241
|
end
|
225
242
|
|
226
|
-
|
243
|
+
use_ssl = ENV['SSL'] == 'true'
|
244
|
+
port = use_ssl ? 9293 : 9292
|
245
|
+
protocol = use_ssl ? 'https' : 'http'
|
246
|
+
|
247
|
+
logger.info("Starting MCP #{protocol.upcase} Development Server on #{protocol}://localhost:#{port}/mcp")
|
227
248
|
|
228
249
|
app = Rack::Builder.new do
|
229
250
|
map '/mcp' do
|
@@ -231,7 +252,30 @@ app = Rack::Builder.new do
|
|
231
252
|
end
|
232
253
|
end
|
233
254
|
|
234
|
-
|
255
|
+
server_options = {
|
256
|
+
Port: port,
|
257
|
+
Host: '0.0.0.0'
|
258
|
+
}
|
259
|
+
|
260
|
+
if use_ssl
|
261
|
+
cert_path = File.join(__dir__, '..', 'tmp', 'ssl', 'server.crt')
|
262
|
+
key_path = File.join(__dir__, '..', 'tmp', 'ssl', 'server.key')
|
263
|
+
|
264
|
+
unless File.exist?(cert_path) && File.exist?(key_path)
|
265
|
+
logger.error("SSL certificates not found at tmp/ssl/")
|
266
|
+
logger.error("Generate them with: openssl req -x509 -newkey rsa:4096 -keyout tmp/ssl/server.key -out tmp/ssl/server.crt -days 365 -nodes -subj \"/C=US/ST=Dev/L=Dev/O=Dev/CN=localhost\"")
|
267
|
+
exit(1)
|
268
|
+
end
|
269
|
+
|
270
|
+
server_options.merge!(
|
271
|
+
SSLEnable: true,
|
272
|
+
SSLCertificate: OpenSSL::X509::Certificate.new(File.read(cert_path)),
|
273
|
+
SSLPrivateKey: OpenSSL::PKey::RSA.new(File.read(key_path)),
|
274
|
+
SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE
|
275
|
+
)
|
276
|
+
end
|
277
|
+
|
278
|
+
server = WEBrick::HTTPServer.new(server_options)
|
235
279
|
server.mount '/', Rackup::Handler::WEBrick, app
|
236
280
|
|
237
281
|
['INT', 'TERM'].each do |signal|
|
@@ -241,4 +285,4 @@ server.mount '/', Rackup::Handler::WEBrick, app
|
|
241
285
|
end
|
242
286
|
end
|
243
287
|
|
244
|
-
server.start
|
288
|
+
server.start
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: model-context-protocol-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dick Davis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json-schema
|
@@ -38,6 +38,48 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: redis
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: connection_pool
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.4'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: concurrent-ruby
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.3'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.3'
|
41
83
|
description:
|
42
84
|
email:
|
43
85
|
- dick@hey.com
|
@@ -56,20 +98,32 @@ files:
|
|
56
98
|
- Rakefile
|
57
99
|
- lib/model_context_protocol.rb
|
58
100
|
- lib/model_context_protocol/server.rb
|
101
|
+
- lib/model_context_protocol/server/cancellable.rb
|
59
102
|
- lib/model_context_protocol/server/completion.rb
|
60
103
|
- lib/model_context_protocol/server/configuration.rb
|
61
104
|
- lib/model_context_protocol/server/content.rb
|
62
105
|
- lib/model_context_protocol/server/content_helpers.rb
|
63
106
|
- lib/model_context_protocol/server/mcp_logger.rb
|
64
107
|
- lib/model_context_protocol/server/pagination.rb
|
108
|
+
- lib/model_context_protocol/server/progressable.rb
|
65
109
|
- lib/model_context_protocol/server/prompt.rb
|
110
|
+
- lib/model_context_protocol/server/redis_client_proxy.rb
|
111
|
+
- lib/model_context_protocol/server/redis_config.rb
|
112
|
+
- lib/model_context_protocol/server/redis_pool_manager.rb
|
66
113
|
- lib/model_context_protocol/server/registry.rb
|
67
114
|
- lib/model_context_protocol/server/resource.rb
|
68
115
|
- lib/model_context_protocol/server/resource_template.rb
|
69
116
|
- lib/model_context_protocol/server/router.rb
|
70
|
-
- lib/model_context_protocol/server/session_store.rb
|
71
117
|
- lib/model_context_protocol/server/stdio_transport.rb
|
118
|
+
- lib/model_context_protocol/server/stdio_transport/request_store.rb
|
72
119
|
- lib/model_context_protocol/server/streamable_http_transport.rb
|
120
|
+
- lib/model_context_protocol/server/streamable_http_transport/event_counter.rb
|
121
|
+
- lib/model_context_protocol/server/streamable_http_transport/message_poller.rb
|
122
|
+
- lib/model_context_protocol/server/streamable_http_transport/notification_queue.rb
|
123
|
+
- lib/model_context_protocol/server/streamable_http_transport/request_store.rb
|
124
|
+
- lib/model_context_protocol/server/streamable_http_transport/session_message_queue.rb
|
125
|
+
- lib/model_context_protocol/server/streamable_http_transport/session_store.rb
|
126
|
+
- lib/model_context_protocol/server/streamable_http_transport/stream_registry.rb
|
73
127
|
- lib/model_context_protocol/server/tool.rb
|
74
128
|
- lib/model_context_protocol/version.rb
|
75
129
|
- tasks/mcp.rake
|