diff --git a/lib/mcp/server/transports/streamable_http_transport.rb b/lib/mcp/server/transports/streamable_http_transport.rb index 688e38c..88a9a3d 100644 --- a/lib/mcp/server/transports/streamable_http_transport.rb +++ b/lib/mcp/server/transports/streamable_http_transport.rb @@ -267,6 +267,9 @@ def handle_post(request) accept_error = validate_accept_header(request, REQUIRED_POST_ACCEPT_TYPES) return accept_error if accept_error + content_type_error = validate_content_type(request) + return content_type_error if content_type_error + body_string = request.body.read session_id = extract_session_id(request) @@ -399,6 +402,18 @@ def parse_accept_header(header) end end + def validate_content_type(request) + content_type = request.env["CONTENT_TYPE"] + media_type = content_type&.split(";")&.first&.strip&.downcase + return if media_type == "application/json" + + [ + 415, + { "Content-Type" => "application/json" }, + [{ error: "Unsupported Media Type: Content-Type must be application/json" }.to_json], + ] + end + def not_acceptable_response(required_types) [ 406, diff --git a/test/mcp/server/transports/streamable_http_transport_test.rb b/test/mcp/server/transports/streamable_http_transport_test.rb index 7435289..a462050 100644 --- a/test/mcp/server/transports/streamable_http_transport_test.rb +++ b/test/mcp/server/transports/streamable_http_transport_test.rb @@ -1143,6 +1143,51 @@ def string assert_equal "Method not allowed", body["error"] end + test "POST request without Content-Type returns 415" do + request = create_rack_request_without_accept( + "POST", + "/", + { "HTTP_ACCEPT" => "application/json, text/event-stream" }, + { jsonrpc: "2.0", method: "initialize", id: "123" }.to_json, + ) + + response = @transport.handle_request(request) + assert_equal 415, response[0] + + body = JSON.parse(response[2][0]) + assert_equal "Unsupported Media Type: Content-Type must be application/json", body["error"] + end + + test "POST request with wrong Content-Type returns 415" do + request = create_rack_request_without_accept( + "POST", + "/", + { + "CONTENT_TYPE" => "text/plain", + "HTTP_ACCEPT" => "application/json, text/event-stream", + }, + { jsonrpc: "2.0", method: "initialize", id: "123" }.to_json, + ) + + response = @transport.handle_request(request) + assert_equal 415, response[0] + end + + test "POST request with Content-Type including charset succeeds" do + request = create_rack_request_without_accept( + "POST", + "/", + { + "CONTENT_TYPE" => "application/json; charset=utf-8", + "HTTP_ACCEPT" => "application/json, text/event-stream", + }, + { jsonrpc: "2.0", method: "initialize", id: "123" }.to_json, + ) + + response = @transport.handle_request(request) + assert_equal 200, response[0] + end + test "POST request without Accept header returns 406" do request = create_rack_request_without_accept( "POST",