Skip to content

Invalid UTF-8 in MCP-Protocol-Version, Mcp-Method or Mcp-Name gives 500 instead of 400 #663

Open

Description

@atesgoral

On the modern path, StreamableHTTPTransport puts raw header values into JSON error bodies:

Rack gives header values as binary strings. Also, decode_header_value tags a decoded =?base64?...?= value as UTF-8 without checking it (:1586-1592), so an all-ASCII Mcp-Name: =?base64?/w==?= also produces an invalid string. json_rpc_error_response then calls to_json (:2064-2070), which raises JSON::GeneratorError. The rescue at :968-974 reports the exception and answers 500.

Related: #629 (a reply that cannot be generated gets Internal error) and #609 (another header that caused a 500). Open #654 adds a strict UTF-8 check when it decodes Mcp-Param-* values, but it does not change decode_header_value or these error bodies.

Repro

Run from the repository root with bundle exec ruby -I lib repro.rb:

require "mcp"
require "rack"

reported = []
MCP.configure { |config| config.exception_reporter = ->(error, _context) { reported << error.class } }
server = MCP::Server.new(name: "example")
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)

env = Rack::MockRequest.env_for(
  "http://localhost/",
  method: "POST",
  input: JSON.generate(jsonrpc: "2.0", id: 1, method: "tools/list"),
  "CONTENT_TYPE" => "application/json",
  "HTTP_ACCEPT" => "application/json, text/event-stream",
  "HTTP_MCP_PROTOCOL_VERSION" => "2026-07-28\xFF".b,
)
status, _headers, body = transport.call(env)
p [status, body.join, reported]
transport.close

Expected: 400 with "Unsupported protocol version", and no exception report. For the Mcp-Method and Mcp-Name cases, 400 with the header mismatch error (-32020).

Actual:

[500, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32603,\"message\":\"Internal server error\"}}", [JSON::GeneratorError]]

The same 500 happens with invalid bytes in Mcp-Method or Mcp-Name on a modern tools/call, and with Mcp-Name: =?base64?/w==?=.

Suggested fix

Check header values with valid_encoding? (after tagging them as UTF-8) before using them. Leave invalid values out of error bodies, or scrub them. Make decode_header_value refuse a decoded value that is not valid UTF-8, the same way the codec in #654 does.

Activity

  1. added
    bugSomething isn't working
    on Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions