Merge pull request #546 from koic/parse_json_response_bodies_in_the_client
Parse HTTP client response bodies without Faraday's JSON middleware
This commit is contained in:
commit
d0b22ef722
3 changed files with 27 additions and 3 deletions
3
Gemfile
3
Gemfile
|
|
@ -19,6 +19,9 @@ gem "activesupport"
|
|||
gem "debug" if RUBY_VERSION >= "3.1"
|
||||
# Avoid i18n 1.15.0, which breaks on Ruby 3.1 (ruby-i18n/i18n#735).
|
||||
gem "i18n", "!= 1.15.0"
|
||||
# FIXME: Drop this once a json release includes https://github.com/ruby/json/pull/1072. json 3.0.0 and 3.0.1
|
||||
# forward arguments after a leading parameter, syntax Ruby 2.7.3 was the first to parse, while allowing Ruby 2.7.0.
|
||||
gem "json", "< 3" if RUBY_VERSION < "2.7.3"
|
||||
gem "rake", "~> 13.0"
|
||||
gem "sorbet-static-and-runtime" if RUBY_VERSION >= "3.0"
|
||||
gem "yard", "~> 0.9"
|
||||
|
|
|
|||
|
|
@ -723,7 +723,8 @@ module MCP
|
|||
require_faraday!
|
||||
@client ||= Faraday.new(url) do |faraday|
|
||||
faraday.request(:json)
|
||||
faraday.response(:json)
|
||||
# The client parses response bodies itself (`resolve_response_body`), so streamed and buffered responses
|
||||
# take the same path; Faraday's json response middleware is deliberately left out.
|
||||
faraday.response(:raise_error)
|
||||
|
||||
faraday.headers["Accept"] = ACCEPT_HEADER
|
||||
|
|
@ -1074,8 +1075,8 @@ module MCP
|
|||
elsif content_type&.include?("application/json")
|
||||
return parse_json_buffer(stream.buffer, method, params) unless stream.buffer.empty?
|
||||
|
||||
# Adapters without `on_data` support deliver the body via `response.body`,
|
||||
# already parsed by the json response middleware.
|
||||
# Adapters without `on_data` support deliver the body via `response.body`; a JSON middleware
|
||||
# added by the Faraday customizer may have parsed it already.
|
||||
response.body.is_a?(String) ? parse_json_buffer(response.body, method, params) : response.body
|
||||
else
|
||||
raise RequestHandlerError.new(
|
||||
|
|
|
|||
|
|
@ -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