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.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +337 -158
  4. data/lib/model_context_protocol/server/cancellable.rb +54 -0
  5. data/lib/model_context_protocol/server/configuration.rb +4 -9
  6. data/lib/model_context_protocol/server/progressable.rb +72 -0
  7. data/lib/model_context_protocol/server/prompt.rb +3 -1
  8. data/lib/model_context_protocol/server/redis_client_proxy.rb +134 -0
  9. data/lib/model_context_protocol/server/redis_config.rb +108 -0
  10. data/lib/model_context_protocol/server/redis_pool_manager.rb +110 -0
  11. data/lib/model_context_protocol/server/resource.rb +3 -0
  12. data/lib/model_context_protocol/server/router.rb +36 -3
  13. data/lib/model_context_protocol/server/stdio_transport/request_store.rb +102 -0
  14. data/lib/model_context_protocol/server/stdio_transport.rb +31 -6
  15. data/lib/model_context_protocol/server/streamable_http_transport/event_counter.rb +35 -0
  16. data/lib/model_context_protocol/server/streamable_http_transport/message_poller.rb +101 -0
  17. data/lib/model_context_protocol/server/streamable_http_transport/notification_queue.rb +80 -0
  18. data/lib/model_context_protocol/server/streamable_http_transport/request_store.rb +224 -0
  19. data/lib/model_context_protocol/server/streamable_http_transport/session_message_queue.rb +120 -0
  20. data/lib/model_context_protocol/server/{session_store.rb → streamable_http_transport/session_store.rb} +30 -16
  21. data/lib/model_context_protocol/server/streamable_http_transport/stream_registry.rb +119 -0
  22. data/lib/model_context_protocol/server/streamable_http_transport.rb +181 -80
  23. data/lib/model_context_protocol/server/tool.rb +4 -0
  24. data/lib/model_context_protocol/server.rb +9 -3
  25. data/lib/model_context_protocol/version.rb +1 -1
  26. data/tasks/templates/dev-http.erb +58 -14
  27. 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: env,
102
- redis_client: redis_client,
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
- logger.info("Starting MCP HTTP Development Server on http://localhost:9292/mcp")
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
- server = WEBrick::HTTPServer.new(Port: 9292, Host: '0.0.0.0')
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.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-08 00:00:00.000000000 Z
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