Skip to content

Commit 0b51173

Browse files
authored
Merge pull request #460 from koic/lowercase_response_header_names
Lowercase response header names in `StreamableHTTPTransport`
2 parents b1b50da + 991926a commit 0b51173

5 files changed

Lines changed: 196 additions & 154 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This is the official Ruby SDK for the Model Context Protocol (MCP), implementing
3232
- Use frozen string literals
3333
- Follow Ruby community conventions
3434
- Keep dependencies minimal
35+
- Use lowercase HTTP response header names (e.g. `mcp-session-id`); the Rack 3 SPEC requires this, and the MCP spec's `Mcp-Session-Id` casing is prose convention only
3536

3637
## Commit message conventions
3738

examples/streamable_http_server.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def call(env)
105105
@mcp_logger.error("Response error: #{parsed["error"]["message"]}")
106106
else
107107
@mcp_logger.info("Response: success (id: #{parsed["id"]})")
108-
@sse_logger.info("Session created: #{headers["Mcp-Session-Id"]}") if headers["Mcp-Session-Id"]
108+
@sse_logger.info("Session created: #{headers["mcp-session-id"]}") if headers["mcp-session-id"]
109109
end
110110
rescue JSON::ParserError
111111
@mcp_logger.warn("Invalid JSON in response")

lib/mcp/server/transports/streamable_http_transport.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ class StreamableHTTPTransport < Transport
1919
class InvalidJsonError < StandardError; end
2020

2121
SSE_HEADERS = {
22-
"Content-Type" => "text/event-stream",
23-
"Cache-Control" => "no-cache",
24-
"Connection" => "keep-alive",
22+
"content-type" => "text/event-stream",
23+
"cache-control" => "no-cache",
24+
"connection" => "keep-alive",
2525
}.freeze
2626

2727
# Secure defaults for stateful mode. Without a finite idle timeout, sessions live until an explicit client DELETE,
@@ -566,7 +566,7 @@ def handle_get(request)
566566
end
567567

568568
def handle_delete(request)
569-
success_response = [200, { "Content-Type" => "application/json" }, [{ success: true }.to_json]]
569+
success_response = [200, { "content-type" => "application/json" }, [{ success: true }.to_json]]
570570

571571
if @stateless
572572
protocol_version_error = validate_protocol_version_header(request)
@@ -762,7 +762,7 @@ def validate_protocol_version_header(request)
762762

763763
def json_rpc_error_response(status:, code:, message:)
764764
body = { jsonrpc: "2.0", id: nil, error: { code: code, message: message } }
765-
[status, { "Content-Type" => "application/json" }, [body.to_json]]
765+
[status, { "content-type" => "application/json" }, [body.to_json]]
766766
end
767767

768768
def notification?(body)
@@ -870,10 +870,10 @@ def handle_initialization(request, body_string, body)
870870
end
871871

872872
headers = {
873-
"Content-Type" => "application/json",
873+
"content-type" => "application/json",
874874
}
875875

876-
headers["Mcp-Session-Id"] = session_id if session_id
876+
headers["mcp-session-id"] = session_id if session_id
877877

878878
[200, headers, [response]]
879879
end
@@ -915,7 +915,7 @@ def handle_regular_request(body_string, session_id, related_request_id: nil)
915915
# which would produce an empty body the client cannot parse as JSON.
916916
return handle_accepted if response.nil?
917917

918-
[200, { "Content-Type" => "application/json" }, [response]]
918+
[200, { "content-type" => "application/json" }, [response]]
919919
end
920920
end
921921

@@ -1148,7 +1148,7 @@ def invalid_request_response(message, request_id: nil)
11481148
message: message,
11491149
},
11501150
}
1151-
[400, { "Content-Type" => "application/json" }, [body.to_json]]
1151+
[400, { "content-type" => "application/json" }, [body.to_json]]
11521152
end
11531153

11541154
def session_already_connected_response

test/mcp/server/transports/streamable_http_notification_integration_test.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
2828
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
2929
)
3030
init_response = @transport.handle_request(init_request)
31-
session_id = init_response[1]["Mcp-Session-Id"]
31+
session_id = init_response[1]["mcp-session-id"]
3232

3333
# Connect with SSE
3434
io = StringIO.new
@@ -77,7 +77,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
7777
{ jsonrpc: "2.0", method: "initialize", id: "123", params: initialize_params }.to_json,
7878
)
7979
init_response1 = @transport.handle_request(init_request1)
80-
session_id1 = init_response1[1]["Mcp-Session-Id"]
80+
session_id1 = init_response1[1]["mcp-session-id"]
8181

8282
init_request2 = create_rack_request(
8383
"POST",
@@ -86,7 +86,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
8686
{ jsonrpc: "2.0", method: "initialize", id: "456", params: initialize_params }.to_json,
8787
)
8888
init_response2 = @transport.handle_request(init_request2)
89-
session_id2 = init_response2[1]["Mcp-Session-Id"]
89+
session_id2 = init_response2[1]["mcp-session-id"]
9090

9191
# Connect both sessions with SSE
9292
io1 = StringIO.new
@@ -132,7 +132,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
132132
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
133133
)
134134
init_response = @transport.handle_request(init_request)
135-
session_id = init_response[1]["Mcp-Session-Id"]
135+
session_id = init_response[1]["mcp-session-id"]
136136

137137
# Connect with SSE
138138
io = StringIO.new
@@ -168,7 +168,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
168168
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
169169
)
170170
init_response = @transport.handle_request(init_request)
171-
session_id = init_response[1]["Mcp-Session-Id"]
171+
session_id = init_response[1]["mcp-session-id"]
172172

173173
# Connect with SSE
174174
io = StringIO.new
@@ -212,7 +212,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
212212
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
213213
)
214214
init_response = @transport.handle_request(init_request)
215-
session_id = init_response[1]["Mcp-Session-Id"]
215+
session_id = init_response[1]["mcp-session-id"]
216216

217217
# Connect with SSE
218218
io = StringIO.new

0 commit comments

Comments
 (0)