Skip to content

Conversation

@ericproulx
Copy link
Contributor

@ericproulx ericproulx commented Sep 29, 2025

Fix Rack compatibility issues across versions 2.x and 3.x

Summary

This PR addresses compatibility issues between Rack 2.x and 3.x versions in the FastMCP transport layer. The changes ensure that the gem works correctly with both major versions of Rack while maintaining backward compatibility.

Problem

The codebase had several incompatibilities with different Rack versions:

  1. Header handling: Rack 3.x introduces Rack::Headers while Rack 2.x uses Rack::Utils::HeaderHash
  2. Hijack API: The rack.hijack interface changed between versions
  3. Request body handling: Some edge cases around body rewinding needed to be addressed
  4. Test infrastructure: Tests were not properly using Rack's mock request/response utilities

Solution

1. Dynamic Header Class Selection

  • Added version detection to use the appropriate header class based on Rack version
  • Properly freeze and duplicate headers to prevent modification issues
  • Consolidated header definitions into constants for consistency

2. Rack Hijack Compatibility

  • Fixed SSE connection handling to work with both Rack 2.x and 3.x
  • Rack 3.x: rack.hijack returns IO directly
  • Rack 2.x: IO is available via rack.hijack_io after calling rack.hijack

3. Request Body Safety

  • Added safety check for rewind method availability on request body
  • Prevents errors when dealing with non-rewindable streams

4. Improved Test Infrastructure

  • Introduced custom RSpec matchers for cleaner and more maintainable tests:
    • be_json_rpc_response: Validates general JSON-RPC 2.0 responses
    • be_json_rpc_error: Specifically tests error responses with chainable assertions
    • be_default_ok_response: Tests plain text OK responses
  • Updated all transport specs to use Rack::MockRequest and Rack::MockResponse
  • Added Rack::Lint middleware in tests to ensure Rack compliance

Changes

Modified Files

  • lib/mcp/transports/rack_transport.rb: Main compatibility fixes
  • lib/mcp/transports/authenticated_rack_transport.rb: Header handling updates
  • spec/mcp/transports/*_spec.rb: Complete test suite overhaul
  • spec/spec_helper.rb: Added support file loading

New Files

  • spec/support/json_rpc_response_matcher.rb: General JSON-RPC response matcher
  • spec/support/json_rpc_error_matcher.rb: Error-specific matcher with chaining
  • spec/support/default_ok_response_matcher.rb: Plain text response matcher

Testing

All tests have been updated and verified to pass with both:

  • Rack 2.x (tested with 2.2.x)
  • Rack 3.x (tested with 3.0.x and 3.1.x)

The custom matchers provide better test readability and more informative failure messages:

# Before
expect(result[0]).to eq(401)
expect(JSON.parse(result[2].first)['error']['code']).to eq(-32_000)

# After
expect(result).to be_json_rpc_error
  .with_error_code(-32_000)
  .with_message('Unauthorized: Invalid or missing authentication token')
  .with_status(401)

Breaking Changes

None. All changes maintain backward compatibility.

Future Considerations

  • The header handling approach could be extracted into a separate compatibility module if more Rack version differences emerge
  • Consider adding CI matrix testing for multiple Rack versions
  • The custom matchers could be packaged as a separate testing utility gem if they prove useful in other contexts

- Update Ruby version to 3.4.2 in .ruby-version
- Fix header handling to use proper Rack header classes based on version
  - Use Rack::Headers for Rack 3.x and Rack::Utils::HeaderHash for Rack 2.x
  - Ensure headers are properly frozen and duplicated when used
- Fix rack.hijack handling for SSE connections
  - Rack 3.x returns IO directly from hijack call
  - Rack 2.x uses separate hijack_io property
- Add support for request body rewind safety check
- Improve test infrastructure with custom RSpec matchers:
  - be_json_rpc_response: General JSON-RPC 2.0 response matcher
  - be_json_rpc_error: Specific error response matcher with chaining
  - be_default_ok_response: Plain text OK response matcher
- Update all transport specs to use proper Rack::MockRequest and matchers
- Ensure all tests pass under both Rack 2.x and 3.x versions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant