Skip to content

Commit 17334c2

Browse files
authored
Merge pull request #304 from koic/validate_post_content_type
Validate Content-Type on POST requests
2 parents 89281ca + 81e81c3 commit 17334c2

2 files changed

Lines changed: 60 additions & 0 deletions

File tree

lib/mcp/server/transports/streamable_http_transport.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ def handle_post(request)
267267
accept_error = validate_accept_header(request, REQUIRED_POST_ACCEPT_TYPES)
268268
return accept_error if accept_error
269269

270+
content_type_error = validate_content_type(request)
271+
return content_type_error if content_type_error
272+
270273
body_string = request.body.read
271274
session_id = extract_session_id(request)
272275

@@ -399,6 +402,18 @@ def parse_accept_header(header)
399402
end
400403
end
401404

405+
def validate_content_type(request)
406+
content_type = request.env["CONTENT_TYPE"]
407+
media_type = content_type&.split(";")&.first&.strip&.downcase
408+
return if media_type == "application/json"
409+
410+
[
411+
415,
412+
{ "Content-Type" => "application/json" },
413+
[{ error: "Unsupported Media Type: Content-Type must be application/json" }.to_json],
414+
]
415+
end
416+
402417
def not_acceptable_response(required_types)
403418
[
404419
406,

test/mcp/server/transports/streamable_http_transport_test.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,51 @@ def string
11431143
assert_equal "Method not allowed", body["error"]
11441144
end
11451145

1146+
test "POST request without Content-Type returns 415" do
1147+
request = create_rack_request_without_accept(
1148+
"POST",
1149+
"/",
1150+
{ "HTTP_ACCEPT" => "application/json, text/event-stream" },
1151+
{ jsonrpc: "2.0", method: "initialize", id: "123" }.to_json,
1152+
)
1153+
1154+
response = @transport.handle_request(request)
1155+
assert_equal 415, response[0]
1156+
1157+
body = JSON.parse(response[2][0])
1158+
assert_equal "Unsupported Media Type: Content-Type must be application/json", body["error"]
1159+
end
1160+
1161+
test "POST request with wrong Content-Type returns 415" do
1162+
request = create_rack_request_without_accept(
1163+
"POST",
1164+
"/",
1165+
{
1166+
"CONTENT_TYPE" => "text/plain",
1167+
"HTTP_ACCEPT" => "application/json, text/event-stream",
1168+
},
1169+
{ jsonrpc: "2.0", method: "initialize", id: "123" }.to_json,
1170+
)
1171+
1172+
response = @transport.handle_request(request)
1173+
assert_equal 415, response[0]
1174+
end
1175+
1176+
test "POST request with Content-Type including charset succeeds" do
1177+
request = create_rack_request_without_accept(
1178+
"POST",
1179+
"/",
1180+
{
1181+
"CONTENT_TYPE" => "application/json; charset=utf-8",
1182+
"HTTP_ACCEPT" => "application/json, text/event-stream",
1183+
},
1184+
{ jsonrpc: "2.0", method: "initialize", id: "123" }.to_json,
1185+
)
1186+
1187+
response = @transport.handle_request(request)
1188+
assert_equal 200, response[0]
1189+
end
1190+
11461191
test "POST request without Accept header returns 406" do
11471192
request = create_rack_request_without_accept(
11481193
"POST",

0 commit comments

Comments
 (0)