Skip to content

HTTP client fails connect when the server acknowledges notifications/initialized with an empty 200 #598

Closed

Description

@dtjldamien

Observed behavior

With mcp 1.7.0 (and 0.15.0), MCP::Client#connect with the legacy initialize handshake over Streamable HTTP fails against servers that acknowledge notifications/initialized with 200 OK, an empty body and no Content-Type, instead of 202 Accepted:

MCP::Client::RequestHandlerError: Unsupported Content-Type: nil. Expected application/json or text/event-stream.
error_type: :unsupported_media_type

The initialize request succeeds. The error comes from the following notifications/initialized POST: send_request passes the response to resolve_response_body, which returns early only for 202 and raises for any other response without a JSON or SSE content type.

For example, Freshservice's hosted MCP server (Freshworks, https://<account>.freshservice.com/mcp) responds this way:

POST /mcp  {"jsonrpc":"2.0","method":"notifications/initialized"}

HTTP/1.1 200 OK
Content-Length: 0

I ran each SDK's client against the same Freshservice server, with the same OAuth token, on 2026-10-13:

Client notifications/initialized response Result
TypeScript SDK 1.32.1 (StreamableHTTPClientTransport) 200, no Content-Type Connects, listTools returns 40 tools
Python SDK 2.3.0 (streamable_http_client) 200, no Content-Type Connects, list_tools returns 40 tools
Ruby SDK main (7735046) 200, no Content-Type Unsupported Content-Type: nil

Expected behavior

The spec says the server MUST return 202 Accepted with no body when it accepts a notification (Transports, Sending Messages to the Server), so the server is out of spec here. Still, a notification awaits no response, and both reference SDKs accept any 2xx for it:

In both, a request answered the same way is still an error: TypeScript throws StreamableHTTPError and Python returns a JSON-RPC error, each with "Unexpected content type".

I'd expect connect to succeed when a notification is acknowledged with a 2xx and no body. Requests should keep raising unsupported_media_type for the same response.

Reproduction

require "mcp"
require "webmock"
include WebMock::API
WebMock.enable!

url = "https://example.com/mcp"
stub_request(:post, url)
  .with { |req| JSON.parse(req.body)["method"] == "initialize" }
  .to_return(
    status: 200,
    headers: { "Content-Type" => "application/json" },
    body: { jsonrpc: "2.0", id: "1", result: { protocolVersion: "2025-11-25", capabilities: {}, serverInfo: { name: "s", version: "1" } } }.to_json,
  )
stub_request(:post, url)
  .with { |req| JSON.parse(req.body)["method"] == "notifications/initialized" }
  .to_return(status: 200, body: "")

MCP::Client.new(transport: MCP::Client::HTTP.new(url: url)).connect(mode: :legacy)
# => MCP::Client::RequestHandlerError: Unsupported Content-Type: nil. ...

Fix: #599. It returns nil instead of raising in that branch when the message is a notification and the status is 2xx, matching the behaviour above.

Activity

  1. added
    bugSomething isn't working
    on Oct 13, 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