Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ This is the official Ruby SDK for the Model Context Protocol (MCP), implementing
- Use frozen string literals
- Follow Ruby community conventions
- Keep dependencies minimal
- 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

## Commit message conventions

Expand Down
2 changes: 1 addition & 1 deletion examples/streamable_http_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def call(env)
@mcp_logger.error("Response error: #{parsed["error"]["message"]}")
else
@mcp_logger.info("Response: success (id: #{parsed["id"]})")
@sse_logger.info("Session created: #{headers["Mcp-Session-Id"]}") if headers["Mcp-Session-Id"]
@sse_logger.info("Session created: #{headers["mcp-session-id"]}") if headers["mcp-session-id"]
end
rescue JSON::ParserError
@mcp_logger.warn("Invalid JSON in response")
Expand Down
18 changes: 9 additions & 9 deletions lib/mcp/server/transports/streamable_http_transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class StreamableHTTPTransport < Transport
class InvalidJsonError < StandardError; end

SSE_HEADERS = {
"Content-Type" => "text/event-stream",
"Cache-Control" => "no-cache",
"Connection" => "keep-alive",
"content-type" => "text/event-stream",
"cache-control" => "no-cache",
"connection" => "keep-alive",
}.freeze

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

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

if @stateless
protocol_version_error = validate_protocol_version_header(request)
Expand Down Expand Up @@ -762,7 +762,7 @@ def validate_protocol_version_header(request)

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

def notification?(body)
Expand Down Expand Up @@ -870,10 +870,10 @@ def handle_initialization(request, body_string, body)
end

headers = {
"Content-Type" => "application/json",
"content-type" => "application/json",
}

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

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

[200, { "Content-Type" => "application/json" }, [response]]
[200, { "content-type" => "application/json" }, [response]]
end
end

Expand Down Expand Up @@ -1148,7 +1148,7 @@ def invalid_request_response(message, request_id: nil)
message: message,
},
}
[400, { "Content-Type" => "application/json" }, [body.to_json]]
[400, { "content-type" => "application/json" }, [body.to_json]]
end

def session_already_connected_response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
)
init_response = @transport.handle_request(init_request)
session_id = init_response[1]["Mcp-Session-Id"]
session_id = init_response[1]["mcp-session-id"]

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

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

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

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

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

# Connect with SSE
io = StringIO.new
Expand Down
Loading