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

Open Beginner friendly
#663 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@atesgoral is already working on this.

Since Nov 6, 2026.

  • #670 by @atesgoral — open
  • #671 by @koic — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
ruby

Research direction

Start with lib/mcp/server/transports/streamable_http_transport.rb, especially the cited header handling, decode_header_value, and json_rpc_error_response paths. Reproduce with the supplied Rack request and inspect nearby transport tests for invalid header values. Done means invalid UTF-8 in these headers returns the expected 400 response without a JSON generator exception or exception report.

Written by the indexing model from the issue text.

Description

bug

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.

Dominant language
Ruby
Stars
940
Forks
143
Avg merge
1d 3h
Merged PRs (30d)
47

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from modelcontextprotocol/ruby-sdk

All issues in modelcontextprotocol/ruby-sdk

Similar issues

More Ruby issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.