Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lib/mcp/server/transports/streamable_http_transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,14 @@ def handle_post(request)

if body["method"] == "initialize"
handle_initialization(body_string, body)
elsif notification?(body) || response?(body)
handle_accepted
else
handle_regular_request(body_string, session_id)
return missing_session_id_response if !@stateless && !session_id

if notification?(body) || response?(body)
handle_accepted
else
handle_regular_request(body_string, session_id)
end
end
rescue StandardError => e
MCP.configuration.exception_reporter.call(e, { request: body_string })
Expand Down
56 changes: 56 additions & 0 deletions test/mcp/server/transports/streamable_http_transport_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,62 @@ class StreamableHTTPTransportTest < ActiveSupport::TestCase
assert_equal "Missing session ID", body["error"]
end

test "rejects POST request without session ID in stateful mode" do
request = create_rack_request(
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "ping", id: "1" }.to_json,
)

response = @transport.handle_request(request)
assert_equal 400, response[0]
body = JSON.parse(response[2][0])
assert_equal "Missing session ID", body["error"]
end

test "rejects notification without session ID in stateful mode" do
request = create_rack_request(
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "notifications/initialized" }.to_json,
)

response = @transport.handle_request(request)
assert_equal 400, response[0]
body = JSON.parse(response[2][0])
assert_equal "Missing session ID", body["error"]
end

test "rejects response without session ID in stateful mode" do
request = create_rack_request(
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", id: "1", result: {} }.to_json,
)

response = @transport.handle_request(request)
assert_equal 400, response[0]
body = JSON.parse(response[2][0])
assert_equal "Missing session ID", body["error"]
end

test "allows POST request without session ID in stateless mode" do
stateless_transport = StreamableHTTPTransport.new(@server, stateless: true)

request = create_rack_request(
"POST",
"/",
{ "CONTENT_TYPE" => "application/json" },
{ jsonrpc: "2.0", method: "ping", id: "1" }.to_json,
)

response = stateless_transport.handle_request(request)
assert_equal 200, response[0]
end

test "rejects duplicate SSE connection with 409" do
# Create a session
init_request = create_rack_request(
Expand Down