Parse HTTP client response bodies without Faraday's JSON middleware - #546
Merged
koic merged 2 commits intoSep 8, 2026
Merged
Conversation
koic
force-pushed
the
parse_json_response_bodies_in_the_client
branch
2 times, most recently
from
September 8, 2026 09:07
164e971 to
40c0e71
Compare
## 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`.
## Motivation and Context json 3.0 declares Ruby 2.7 as its floor, but its `load_file` forwards arguments after a leading parameter, `def load_file(filespec, ...)`, syntax Ruby 2.7.3 was the first to parse. On Ruby 2.7.0 through 2.7.2, requiring json 3.0.1 is a `SyntaxError`, so `require "mcp"` fails before any test runs, and the CI job pinned to 2.7.0, the minimum the gemspec supports, fails the whole suite at load time. The Gemfile keeps json below 3.0 on those three patch levels only. Later 2.7 releases and Ruby 3 parse the syntax and stay on whatever json the resolver picks. The line carries a FIXME to drop it once a json release includes ruby/json#1072, whichever version that turns out to be; excluding only the two broken releases would silently trust a 3.0.2 that may not carry the fix. ## How Has This Been Tested? `gem "json", "3.0.1"; require "json"` raises the `SyntaxError` on Ruby 2.7.0 and loads on 2.7.3 and 2.7.8. With the pin, a fresh `bundle install` on Ruby 2.7.2 resolves json 2.x and `bundle exec rake test` passes. ## Breaking Changes None. The Gemfile is not part of the gem.
koic
force-pushed
the
parse_json_response_bodies_in_the_client
branch
from
September 8, 2026 09:09
40c0e71 to
d40d901
Compare
Member
Author
|
I'll merge this due to the CI failure. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Context
json 3.0, released on 2026-09-07, accepts the options of
JSON.parseas 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, andMCP::Client::HTTPreports 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_bufferand a malformed one answers:parse_errorwhichever 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.json 3.0 also declares Ruby 2.7 as its floor, but its
load_fileforwards arguments after a leading parameter,def load_file(filespec, ...), syntax Ruby 2.7.3 was the first to parse. On Ruby 2.7.0 through 2.7.2, requiring json 3.0.1 is aSyntaxError, sorequire "mcp"fails before any test runs, and the CI job pinned to 2.7.0, the minimum the gemspec supports, fails the whole suite at load time. The Gemfile keeps json below 3.0 on those three patch levels only. Later 2.7 releases and Ruby 3 parse the syntax and stay on whatever json the resolver picks. The line carries a FIXME to drop it once a json release includes ruby/json#1072, whichever version that turns out to be; excluding only the two broken releases would silently trust a 3.0.2 that may not carry the fix.How Has This Been Tested?
A new test drives a buffered JSON response through a stand-in
JSON.parsewith 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.gem "json", "3.0.1"; require "json"raises theSyntaxErroron Ruby 2.7.0 and loads on 2.7.3 and 2.7.8. With the pin, a freshbundle installon Ruby 2.7.2 resolves json 2.x andbundle exec rake testpasses.Breaking Changes
None. A malformed JSON body delivered by an adapter without streaming support now raises
RequestHandlerErrorwitherror_type: :parse_error, as the streamed path already did, instead of:internal_errorwrapping aFaraday::ParsingError. The Gemfile is not part of the gem.Types of changes
Checklist