@@ -8,11 +8,13 @@ module MCP
8
8
class Server
9
9
module Transports
10
10
class StreamableHTTPTransport < Transport
11
- def initialize ( server )
12
- super
11
+ def initialize ( server , stateless : false )
12
+ super ( server )
13
13
# { session_id => { stream: stream_object }
14
14
@sessions = { }
15
15
@mutex = Mutex . new
16
+
17
+ @stateless = stateless
16
18
end
17
19
18
20
def handle_request ( request )
@@ -24,7 +26,7 @@ def handle_request(request)
24
26
when "DELETE"
25
27
handle_delete ( request )
26
28
else
27
- [ 405 , { "Content-Type" => "application/json" } , [ { error : "Method not allowed" } . to_json ] ]
29
+ method_not_allowed_response
28
30
end
29
31
end
30
32
@@ -35,6 +37,9 @@ def close
35
37
end
36
38
37
39
def send_notification ( method , params = nil , session_id : nil )
40
+ # Stateless mode doesn't support notifications
41
+ raise "Stateless mode does not support notifications" if @stateless
42
+
38
43
notification = {
39
44
jsonrpc : "2.0" ,
40
45
method :,
@@ -117,6 +122,10 @@ def handle_post(request)
117
122
end
118
123
119
124
def handle_get ( request )
125
+ if @stateless
126
+ return method_not_allowed_response
127
+ end
128
+
120
129
session_id = extract_session_id ( request )
121
130
122
131
return missing_session_id_response unless session_id
@@ -126,6 +135,13 @@ def handle_get(request)
126
135
end
127
136
128
137
def handle_delete ( request )
138
+ success_response = [ 200 , { "Content-Type" => "application/json" } , [ { success : true } . to_json ] ]
139
+
140
+ if @stateless
141
+ # Stateless mode doesn't support sessions, so we can just return a success response
142
+ return success_response
143
+ end
144
+
129
145
session_id = request . env [ "HTTP_MCP_SESSION_ID" ]
130
146
131
147
return [
@@ -135,7 +151,7 @@ def handle_delete(request)
135
151
] unless session_id
136
152
137
153
cleanup_session ( session_id )
138
- [ 200 , { "Content-Type" => "application/json" } , [ { success : true } . to_json ] ]
154
+ success_response
139
155
end
140
156
141
157
def cleanup_session ( session_id )
@@ -167,31 +183,40 @@ def parse_request_body(body_string)
167
183
end
168
184
169
185
def handle_initialization ( body_string , body )
170
- session_id = SecureRandom . uuid
186
+ session_id = nil
171
187
172
- @mutex . synchronize do
173
- @sessions [ session_id ] = {
174
- stream : nil ,
175
- }
188
+ unless @stateless
189
+ session_id = SecureRandom . uuid
190
+
191
+ @mutex . synchronize do
192
+ @sessions [ session_id ] = {
193
+ stream : nil ,
194
+ }
195
+ end
176
196
end
177
197
178
198
response = @server . handle_json ( body_string )
179
199
180
200
headers = {
181
201
"Content-Type" => "application/json" ,
182
- "Mcp-Session-Id" => session_id ,
183
202
}
184
203
204
+ headers [ "Mcp-Session-Id" ] = session_id if session_id
205
+
185
206
[ 200 , headers , [ response ] ]
186
207
end
187
208
188
209
def handle_regular_request ( body_string , session_id )
189
- # If session ID is provided, but not in the sessions hash, return an error
190
- if session_id && !@sessions . key? ( session_id )
191
- return [ 400 , { "Content-Type" => "application/json" } , [ { error : "Invalid session ID" } . to_json ] ]
210
+ unless @stateless
211
+ # If session ID is provided, but not in the sessions hash, return an error
212
+ if session_id && !@sessions . key? ( session_id )
213
+ return [ 400 , { "Content-Type" => "application/json" } , [ { error : "Invalid session ID" } . to_json ] ]
214
+ end
192
215
end
193
216
194
217
response = @server . handle_json ( body_string )
218
+
219
+ # Stream can be nil since stateless mode doesn't retain streams
195
220
stream = get_session_stream ( session_id ) if session_id
196
221
197
222
if stream
@@ -222,6 +247,10 @@ def session_exists?(session_id)
222
247
@mutex . synchronize { @sessions . key? ( session_id ) }
223
248
end
224
249
250
+ def method_not_allowed_response
251
+ [ 405 , { "Content-Type" => "application/json" } , [ { error : "Method not allowed" } . to_json ] ]
252
+ end
253
+
225
254
def missing_session_id_response
226
255
[ 400 , { "Content-Type" => "application/json" } , [ { error : "Missing session ID" } . to_json ] ]
227
256
end
0 commit comments