Parse HTTP client response bodies without Faraday's JSON middleware
## Motivation and Context json 3.0, released on 2026-09-07, accepts the options of `JSON.parse` as keywords only, and Faraday's json response middleware, through 2.14.3, hands them over as a positional Hash, which Ruby 3 no longer converts. With both installed, every JSON body the client receives fails inside that middleware, and `MCP::Client::HTTP` reports each request as an internal error. Faraday's main branch passes the options as keywords, but no release carries that yet. The client already parses streamed bodies and buffered SSE bodies itself; only a JSON body delivered whole by an adapter without streaming support still went through the middleware. The client now parses that body too and no longer registers the middleware, so every body takes `parse_json_buffer` and a malformed one answers `:parse_error` whichever way it arrived. A JSON middleware added through the Faraday customizer block still works, since a body it has parsed already is used as is. ## How Has This Been Tested? A new test drives a buffered JSON response through a stand-in `JSON.parse` with the json 3.0 signature, options as keywords only, so on Ruby 3 it failed with the middleware registered whatever json version is installed and passes now. The existing buffered-body test keeps covering the plain path. The full suite passes on Ruby 3.0.7 with json 3.0.1 and faraday 2.14.3, where five client tests failed before. ## Breaking Changes None. A malformed JSON body delivered by an adapter without streaming support now raises `RequestHandlerError` with `error_type: :parse_error`, as the streamed path already did, instead of `:internal_error` wrapping a `Faraday::ParsingError`.
This commit is contained in:
parent
44af58c7e0
commit
09c89aef14
1 changed files with 24 additions and 3 deletions
|
|
@ -940,6 +940,26 @@ module MCP
|
|||
assert_equal({ "result" => { "tools" => [] } }, response)
|
||||
end
|
||||
|
||||
def test_send_request_parses_a_json_body_with_a_parser_taking_keyword_options_only
|
||||
# json 3.0 accepts parser options as keywords only, and Faraday's json response middleware
|
||||
# passes them as a positional Hash, which Ruby 3 no longer converts. The stand-in parser has
|
||||
# the json 3.0 signature, so the body must reach `JSON.parse` through the client's own call.
|
||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
||||
stub.post("/") do
|
||||
[200, { "Content-Type" => "application/json" }, { result: { tools: [] } }.to_json]
|
||||
end
|
||||
end
|
||||
client = HTTP.new(url: url) { |faraday| faraday.adapter(:test, stubs) }
|
||||
parse = JSON.method(:parse)
|
||||
keyword_options_only_parse = ->(source, **options) { parse.call(source, **options) }
|
||||
|
||||
response = JSON.stub(:parse, keyword_options_only_parse) do
|
||||
client.send_request(request: { jsonrpc: "2.0", id: "test_id", method: "tools/list" })
|
||||
end
|
||||
|
||||
assert_equal({ "result" => { "tools" => [] } }, response)
|
||||
end
|
||||
|
||||
def test_send_request_mirrors_x_mcp_header_params_into_mcp_param_headers
|
||||
# SEP-2243: on a modern connection, `tools/list` teaches the transport the `x-mcp-header`
|
||||
# declarations, and the following `tools/call` mirrors the annotated arguments into
|
||||
|
|
|
|||
Loading…
Reference in a new issue