fix: accept wildcard media types in Accept header per RFC 7231 (#2152)

Co-authored-by: Shivam <shivam@Shivams-MacBook-Air-2.local>
This commit is contained in:
Shivam Aggarwal 2026-03-09 22:00:02 +05:30 • committed by GitHub
commit 51c53f2c18
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 92 additions and 9 deletions

View file

@ -391,12 +391,19 @@ class StreamableHTTPServerTransport:
await self._handle_unsupported_request(request, send)
def _check_accept_headers(self, request: Request) -> tuple[bool, bool]:
"""Check if the request accepts the required media types."""
accept_header = request.headers.get("accept", "")
accept_types = [media_type.strip() for media_type in accept_header.split(",")]
"""Check if the request accepts the required media types.
has_json = any(media_type.startswith(CONTENT_TYPE_JSON) for media_type in accept_types)
has_sse = any(media_type.startswith(CONTENT_TYPE_SSE) for media_type in accept_types)
Supports wildcard media types per RFC 7231, section 5.3.2:
- */* matches any media type
- application/* matches any application/ subtype
- text/* matches any text/ subtype
"""
accept_header = request.headers.get("accept", "")
accept_types = [media_type.strip().split(";")[0].strip().lower() for media_type in accept_header.split(",")]
has_wildcard = "*/*" in accept_types
has_json = has_wildcard or any(t in (CONTENT_TYPE_JSON, "application/*") for t in accept_types)
has_sse = has_wildcard or any(t in (CONTENT_TYPE_SSE, "text/*") for t in accept_types)
return has_json, has_sse

View file

@ -572,8 +572,10 @@ def json_server_url(json_server_port: int) -> str:
# Basic request validation tests
def test_accept_header_validation(basic_server: None, basic_server_url: str):
"""Test that Accept header is properly validated."""
# Test without Accept header
response = requests.post(
# Test without Accept header (suppress requests library default Accept: */*)
session = requests.Session()
session.headers.pop("Accept")
response = session.post(
f"{basic_server_url}/mcp",
headers={"Content-Type": "application/json"},
json={"jsonrpc": "2.0", "method": "initialize", "id": 1},
@ -582,6 +584,52 @@ def test_accept_header_validation(basic_server: None, basic_server_url: str):
assert "Not Acceptable" in response.text
@pytest.mark.parametrize(
"accept_header",
[
"*/*",
"application/*, text/*",
"text/*, application/json",
"application/json, text/*",
"*/*;q=0.8",
"application/*;q=0.9, text/*;q=0.8",
],
)
def test_accept_header_wildcard(basic_server: None, basic_server_url: str, accept_header: str):
"""Test that wildcard Accept headers are accepted per RFC 7231."""
response = requests.post(
f"{basic_server_url}/mcp",
headers={
"Accept": accept_header,
"Content-Type": "application/json",
},
json=INIT_REQUEST,
)
assert response.status_code == 200
@pytest.mark.parametrize(
"accept_header",
[
"text/html",
"application/*",
"text/*",
],
)
def test_accept_header_incompatible(basic_server: None, basic_server_url: str, accept_header: str):
"""Test that incompatible Accept headers are rejected for SSE mode."""
response = requests.post(
f"{basic_server_url}/mcp",
headers={
"Accept": accept_header,
"Content-Type": "application/json",
},
json=INIT_REQUEST,
)
assert response.status_code == 406
assert "Not Acceptable" in response.text
def test_content_type_validation(basic_server: None, basic_server_url: str):
"""Test that Content-Type header is properly validated."""
# Test with incorrect Content-Type
@ -826,7 +874,10 @@ def test_json_response_accept_json_only(json_response_server: None, json_server_
def test_json_response_missing_accept_header(json_response_server: None, json_server_url: str):
"""Test that json_response servers reject requests without Accept header."""
mcp_url = f"{json_server_url}/mcp"
response = requests.post(
# Suppress requests library default Accept: */* header
session = requests.Session()
session.headers.pop("Accept")
response = session.post(
mcp_url,
headers={
"Content-Type": "application/json",
@ -853,6 +904,29 @@ def test_json_response_incorrect_accept_header(json_response_server: None, json_
assert "Not Acceptable" in response.text
@pytest.mark.parametrize(
"accept_header",
[
"*/*",
"application/*",
"application/*;q=0.9",
],
)
def test_json_response_wildcard_accept_header(json_response_server: None, json_server_url: str, accept_header: str):
"""Test that json_response servers accept wildcard Accept headers per RFC 7231."""
mcp_url = f"{json_server_url}/mcp"
response = requests.post(
mcp_url,
headers={
"Accept": accept_header,
"Content-Type": "application/json",
},
json=INIT_REQUEST,
)
assert response.status_code == 200
assert response.headers.get("Content-Type") == "application/json"
def test_get_sse_stream(basic_server: None, basic_server_url: str):
"""Test establishing an SSE stream via GET request."""
# First, we need to initialize a session
@ -941,8 +1015,10 @@ def test_get_validation(basic_server: None, basic_server_url: str):
assert init_data is not None
negotiated_version = init_data["result"]["protocolVersion"]
# Test without Accept header
response = requests.get(
# Test without Accept header (suppress requests library default Accept: */*)
session = requests.Session()
session.headers.pop("Accept")
response = session.get(
mcp_url,
headers={
MCP_SESSION_ID_HEADER: session_id,