@@ -6327,6 +6327,90 @@ def string
63276327 assert_equal "2027-01-01" , body . dig ( "error" , "data" , "requested" )
63286328 end
63296329
6330+ test "modern POST answers 400 with -32022 when the requested version is not valid UTF-8" do
6331+ # The echoed `requested` member is scrubbed, where `to_json` on the raw value raised and the rescue answered 500 with an exception report.
6332+ reported = [ ]
6333+ original_reporter = MCP . configuration . exception_reporter
6334+ MCP . configuration . exception_reporter = -> ( error , _context ) { reported << error }
6335+ version = "2026-07-28\xFF " . dup . force_encoding ( Encoding ::UTF_8 )
6336+
6337+ response = @transport . handle_request ( modern_rack_request ( modern_body ( "ping" , { } ) , version : version ) )
6338+
6339+ assert_equal 400 , response [ 0 ]
6340+ body = JSON . parse ( response [ 2 ] [ 0 ] )
6341+ assert_equal ( -32022 , body . dig ( "error" , "code" ) )
6342+ assert_equal "2026-07-28\uFFFD " , body . dig ( "error" , "data" , "requested" )
6343+ assert_empty reported
6344+ ensure
6345+ MCP . configuration . exception_reporter = original_reporter
6346+ end
6347+
6348+ test "modern POST answers 400 with -32020 when a mirror header that mismatches is not valid UTF-8" do
6349+ reported = [ ]
6350+ original_reporter = MCP . configuration . exception_reporter
6351+ MCP . configuration . exception_reporter = -> ( error , _context ) { reported << error }
6352+ invalid_method = "tools/call\xFF " . dup . force_encoding ( Encoding ::UTF_8 )
6353+ invalid_name = "=?base64?#{ [ "file:///\xFF " . b ] . pack ( "m0" ) } ?="
6354+
6355+ by_method = @transport . handle_request ( modern_rack_request (
6356+ modern_body ( "ping" , { } ) ,
6357+ headers : { "HTTP_MCP_METHOD" => invalid_method } ,
6358+ ) )
6359+ by_name = @transport . handle_request ( modern_rack_request (
6360+ modern_body ( "resources/read" , { uri : "file:///a.txt" } ) ,
6361+ headers : { "HTTP_MCP_NAME" => invalid_name } ,
6362+ ) )
6363+
6364+ [ by_method , by_name ] . each do |response |
6365+ assert_equal 400 , response [ 0 ]
6366+ body = JSON . parse ( response [ 2 ] [ 0 ] )
6367+ assert_equal ( -32020 , body . dig ( "error" , "code" ) )
6368+ assert_includes body . dig ( "error" , "message" ) , "\uFFFD "
6369+ end
6370+ assert_empty reported
6371+ ensure
6372+ MCP . configuration . exception_reporter = original_reporter
6373+ end
6374+
6375+ test "modern POST answers 400 with -32020 when the body's envelope version is not valid UTF-8" do
6376+ # The body side of the mismatch message comes out of the JSON parser, which passes such bytes through.
6377+ reported = [ ]
6378+ original_reporter = MCP . configuration . exception_reporter
6379+ MCP . configuration . exception_reporter = -> ( error , _context ) { reported << error }
6380+ body_string = modern_body ( "ping" , { } , version : "2026-07-28X" ) . b . sub ( "2026-07-28X" , "2026-07-28\xFF " . b )
6381+
6382+ response = @transport . handle_request ( modern_rack_request ( body_string ) )
6383+
6384+ assert_equal 400 , response [ 0 ]
6385+ body = JSON . parse ( response [ 2 ] [ 0 ] )
6386+ assert_equal ( -32020 , body . dig ( "error" , "code" ) )
6387+ assert_includes body . dig ( "error" , "message" ) , "\uFFFD "
6388+ assert_empty reported
6389+ ensure
6390+ MCP . configuration . exception_reporter = original_reporter
6391+ end
6392+
6393+ test "modern POST echoes a valid non-ASCII mirror header given as bytes in its -32020 message" do
6394+ # A Rack server hands the header over as ASCII-8BIT; json 3.1 refuses to serialize such a string
6395+ # when it holds bytes above 0x7F, valid UTF-8 or not, so the mismatch answered 500 there.
6396+ reported = [ ]
6397+ original_reporter = MCP . configuration . exception_reporter
6398+ MCP . configuration . exception_reporter = -> ( error , _context ) { reported << error }
6399+
6400+ response = @transport . handle_request ( modern_rack_request (
6401+ modern_body ( "ping" , { } ) ,
6402+ headers : { "HTTP_MCP_METHOD" => "p\xC3 \xAF ng" . b } ,
6403+ ) )
6404+
6405+ assert_equal 400 , response [ 0 ]
6406+ body = JSON . parse ( response [ 2 ] [ 0 ] )
6407+ assert_equal ( -32020 , body . dig ( "error" , "code" ) )
6408+ assert_includes body . dig ( "error" , "message" ) , "p\u00EF ng"
6409+ assert_empty reported
6410+ ensure
6411+ MCP . configuration . exception_reporter = original_reporter
6412+ end
6413+
63306414 test "modern POST with a header/body version mismatch returns 400 with -32020" do
63316415 # Header names the supported modern version; the body envelope claims another.
63326416 response = @transport . handle_request ( modern_rack_request (
0 commit comments