Skip to content

Commit 14a7d78

Browse files
committed
Fix to return a response code 202 if the server accepts JSON-RPC notifications and responses
If the server accepts JSON-RPC notifications and responses, it must return an HTTP status code of 202 without a body. refs - https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#sending-messages-to-the-server - https://www.jsonrpc.org/specification Follow up: #111
1 parent 541bbe0 commit 14a7d78

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

lib/mcp/server/transports/streamable_http_transport.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ def handle_post(request)
108108

109109
if body["method"] == "initialize"
110110
handle_initialization(body_string, body)
111-
elsif body["method"] == MCP::Methods::NOTIFICATIONS_INITIALIZED
112-
handle_notification_initialized
111+
elsif notification_or_response?(body)
112+
handle_notification_or_response
113113
else
114114
handle_regular_request(body_string, session_id)
115115
end
@@ -168,6 +168,11 @@ def parse_request_body(body_string)
168168
[400, { "Content-Type" => "application/json" }, [{ error: "Invalid JSON" }.to_json]]
169169
end
170170

171+
def notification_or_response?(body)
172+
# Check if the body is a notification or a response
173+
!(body["id"] && body["method"])
174+
end
175+
171176
def handle_initialization(body_string, body)
172177
session_id = SecureRandom.uuid
173178

@@ -187,7 +192,7 @@ def handle_initialization(body_string, body)
187192
[200, headers, [response]]
188193
end
189194

190-
def handle_notification_initialized
195+
def handle_notification_or_response
191196
[202, {}, []]
192197
end
193198

test/mcp/server/transports/streamable_http_transport_test.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,63 @@ class StreamableHTTPTransportTest < ActiveSupport::TestCase
622622
assert_empty(response[2])
623623
end
624624

625+
test "handles POST request when json-rpc notification object is received" do
626+
notification_request = create_rack_request(
627+
"POST",
628+
"/",
629+
{ "CONTENT_TYPE" => "application/json" },
630+
{ jsonrpc: "2.0", method: "some_notification" }.to_json,
631+
)
632+
633+
response = @transport.handle_request(notification_request)
634+
assert_equal 202, response[0]
635+
assert_empty(response[1])
636+
assert_empty(response[2])
637+
end
638+
639+
test "handles POST request when json-rpc response object with result is received" do
640+
# Test response (has id but no method)
641+
response_request = create_rack_request(
642+
"POST",
643+
"/",
644+
{ "CONTENT_TYPE" => "application/json" },
645+
{ jsonrpc: "2.0", id: "123", result: { content: { type: "text" } } }.to_json,
646+
)
647+
648+
response = @transport.handle_request(response_request)
649+
assert_equal 202, response[0]
650+
assert_empty(response[1])
651+
assert_empty(response[2])
652+
end
653+
654+
test "handles POST request when json-rpc response object with error is received" do
655+
response_request = create_rack_request(
656+
"POST",
657+
"/",
658+
{ "CONTENT_TYPE" => "application/json" },
659+
{ jsonrpc: "2.0", id: "123", error: { content: { type: "text" } } }.to_json,
660+
)
661+
662+
response = @transport.handle_request(response_request)
663+
assert_equal 202, response[0]
664+
assert_empty(response[1])
665+
assert_empty(response[2])
666+
end
667+
668+
test "handles POST request when json-rpc error object is received" do
669+
response_request = create_rack_request(
670+
"POST",
671+
"/",
672+
{ "CONTENT_TYPE" => "application/json" },
673+
{ jsonrpc: "2.0", id: "123", error: { content: { type: "text" } } }.to_json,
674+
)
675+
676+
response = @transport.handle_request(response_request)
677+
assert_equal 202, response[0]
678+
assert_empty(response[1])
679+
assert_empty(response[2])
680+
end
681+
625682
private
626683

627684
def create_rack_request(method, path, headers, body = nil)

0 commit comments

Comments
 (0)