Merge pull request #460 from koic/lowercase_response_header_names
Lowercase response header names in `StreamableHTTPTransport`
This commit is contained in:
commit
0b51173441
5 changed files with 196 additions and 154 deletions
|
|
@ -32,6 +32,7 @@ This is the official Ruby SDK for the Model Context Protocol (MCP), implementing
|
||||||
- Use frozen string literals
|
- Use frozen string literals
|
||||||
- Follow Ruby community conventions
|
- Follow Ruby community conventions
|
||||||
- Keep dependencies minimal
|
- 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
|
## Commit message conventions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class McpSseLogger
|
||||||
@mcp_logger.error("Response error: #{parsed["error"]["message"]}")
|
@mcp_logger.error("Response error: #{parsed["error"]["message"]}")
|
||||||
else
|
else
|
||||||
@mcp_logger.info("Response: success (id: #{parsed["id"]})")
|
@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
|
end
|
||||||
rescue JSON::ParserError
|
rescue JSON::ParserError
|
||||||
@mcp_logger.warn("Invalid JSON in response")
|
@mcp_logger.warn("Invalid JSON in response")
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ module MCP
|
||||||
class InvalidJsonError < StandardError; end
|
class InvalidJsonError < StandardError; end
|
||||||
|
|
||||||
SSE_HEADERS = {
|
SSE_HEADERS = {
|
||||||
"Content-Type" => "text/event-stream",
|
"content-type" => "text/event-stream",
|
||||||
"Cache-Control" => "no-cache",
|
"cache-control" => "no-cache",
|
||||||
"Connection" => "keep-alive",
|
"connection" => "keep-alive",
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# Secure defaults for stateful mode. Without a finite idle timeout, sessions live until an explicit client DELETE,
|
# Secure defaults for stateful mode. Without a finite idle timeout, sessions live until an explicit client DELETE,
|
||||||
|
|
@ -566,7 +566,7 @@ module MCP
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_delete(request)
|
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
|
if @stateless
|
||||||
protocol_version_error = validate_protocol_version_header(request)
|
protocol_version_error = validate_protocol_version_header(request)
|
||||||
|
|
@ -762,7 +762,7 @@ module MCP
|
||||||
|
|
||||||
def json_rpc_error_response(status:, code:, message:)
|
def json_rpc_error_response(status:, code:, message:)
|
||||||
body = { jsonrpc: "2.0", id: nil, error: { code: code, message: 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
|
end
|
||||||
|
|
||||||
def notification?(body)
|
def notification?(body)
|
||||||
|
|
@ -870,10 +870,10 @@ module MCP
|
||||||
end
|
end
|
||||||
|
|
||||||
headers = {
|
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]]
|
[200, headers, [response]]
|
||||||
end
|
end
|
||||||
|
|
@ -915,7 +915,7 @@ module MCP
|
||||||
# which would produce an empty body the client cannot parse as JSON.
|
# which would produce an empty body the client cannot parse as JSON.
|
||||||
return handle_accepted if response.nil?
|
return handle_accepted if response.nil?
|
||||||
|
|
||||||
[200, { "Content-Type" => "application/json" }, [response]]
|
[200, { "content-type" => "application/json" }, [response]]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -1148,7 +1148,7 @@ module MCP
|
||||||
message: message,
|
message: message,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
[400, { "Content-Type" => "application/json" }, [body.to_json]]
|
[400, { "content-type" => "application/json" }, [body.to_json]]
|
||||||
end
|
end
|
||||||
|
|
||||||
def session_already_connected_response
|
def session_already_connected_response
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ module MCP
|
||||||
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
||||||
)
|
)
|
||||||
init_response = @transport.handle_request(init_request)
|
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
|
# Connect with SSE
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
|
|
@ -77,7 +77,7 @@ module MCP
|
||||||
{ jsonrpc: "2.0", method: "initialize", id: "123", params: initialize_params }.to_json,
|
{ jsonrpc: "2.0", method: "initialize", id: "123", params: initialize_params }.to_json,
|
||||||
)
|
)
|
||||||
init_response1 = @transport.handle_request(init_request1)
|
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(
|
init_request2 = create_rack_request(
|
||||||
"POST",
|
"POST",
|
||||||
|
|
@ -86,7 +86,7 @@ module MCP
|
||||||
{ jsonrpc: "2.0", method: "initialize", id: "456", params: initialize_params }.to_json,
|
{ jsonrpc: "2.0", method: "initialize", id: "456", params: initialize_params }.to_json,
|
||||||
)
|
)
|
||||||
init_response2 = @transport.handle_request(init_request2)
|
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
|
# Connect both sessions with SSE
|
||||||
io1 = StringIO.new
|
io1 = StringIO.new
|
||||||
|
|
@ -132,7 +132,7 @@ module MCP
|
||||||
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
||||||
)
|
)
|
||||||
init_response = @transport.handle_request(init_request)
|
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
|
# Connect with SSE
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
|
|
@ -168,7 +168,7 @@ module MCP
|
||||||
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
||||||
)
|
)
|
||||||
init_response = @transport.handle_request(init_request)
|
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
|
# Connect with SSE
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
|
|
@ -212,7 +212,7 @@ module MCP
|
||||||
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
{ jsonrpc: "2.0", method: "initialize", id: "init", params: initialize_params }.to_json,
|
||||||
)
|
)
|
||||||
init_response = @transport.handle_request(init_request)
|
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
|
# Connect with SSE
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue