Skip to content

Commit d079d2c

Browse files
authored
Merge branch 'main' into feat/instructions-docs
2 parents c0d662e + 7ac40af commit d079d2c

File tree

5 files changed

+75
-14
lines changed

5 files changed

+75
-14
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
[protocol-badge]: https://img.shields.io/badge/protocol-modelcontextprotocol.io-blue.svg
8080
[protocol-url]: https://modelcontextprotocol.io
8181
[spec-badge]: https://img.shields.io/badge/spec-spec.modelcontextprotocol.io-blue.svg
82-
[spec-url]: https://spec.modelcontextprotocol.io
82+
[spec-url]: https://modelcontextprotocol.io/specification/latest
8383

8484
## Overview
8585

@@ -2433,7 +2433,7 @@ MCP servers declare capabilities during initialization:
24332433

24342434
- [API Reference](https://modelcontextprotocol.github.io/python-sdk/api/)
24352435
- [Model Context Protocol documentation](https://modelcontextprotocol.io)
2436-
- [Model Context Protocol specification](https://spec.modelcontextprotocol.io)
2436+
- [Model Context Protocol specification](https://modelcontextprotocol.io/specification/latest)
24372437
- [Officially supported servers](https://github.com/modelcontextprotocol/servers)
24382438

24392439
## Contributing

examples/clients/simple-auth-client/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ ignore = []
3939
line-length = 120
4040
target-version = "py310"
4141

42-
[tool.uv]
43-
dev-dependencies = ["pyright>=1.1.379", "pytest>=8.3.3", "ruff>=0.6.9"]
42+
[dependency-groups]
43+
dev = ["pyright>=1.1.379", "pytest>=8.3.3", "ruff>=0.6.9"]

examples/clients/simple-chatbot/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ ignore = []
4444
line-length = 120
4545
target-version = "py310"
4646

47-
[tool.uv]
48-
dev-dependencies = ["pyright>=1.1.379", "pytest>=8.3.3", "ruff>=0.6.9"]
47+
[dependency-groups]
48+
dev = ["pyright>=1.1.379", "pytest>=8.3.3", "ruff>=0.6.9"]

src/mcp/server/streamable_http.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,20 +306,36 @@ def _check_content_type(self, request: Request) -> bool:
306306

307307
return any(part == CONTENT_TYPE_JSON for part in content_type_parts)
308308

309+
async def _validate_accept_header(self, request: Request, scope: Scope, send: Send) -> bool:
310+
"""Validate Accept header based on response mode. Returns True if valid."""
311+
has_json, has_sse = self._check_accept_headers(request)
312+
if self.is_json_response_enabled:
313+
# For JSON-only responses, only require application/json
314+
if not has_json:
315+
response = self._create_error_response(
316+
"Not Acceptable: Client must accept application/json",
317+
HTTPStatus.NOT_ACCEPTABLE,
318+
)
319+
await response(scope, request.receive, send)
320+
return False
321+
# For SSE responses, require both content types
322+
elif not (has_json and has_sse):
323+
response = self._create_error_response(
324+
"Not Acceptable: Client must accept both application/json and text/event-stream",
325+
HTTPStatus.NOT_ACCEPTABLE,
326+
)
327+
await response(scope, request.receive, send)
328+
return False
329+
return True
330+
309331
async def _handle_post_request(self, scope: Scope, request: Request, receive: Receive, send: Send) -> None:
310332
"""Handle POST requests containing JSON-RPC messages."""
311333
writer = self._read_stream_writer
312334
if writer is None:
313335
raise ValueError("No read stream writer available. Ensure connect() is called first.")
314336
try:
315-
# Check Accept headers
316-
has_json, has_sse = self._check_accept_headers(request)
317-
if not (has_json and has_sse):
318-
response = self._create_error_response(
319-
("Not Acceptable: Client must accept both application/json and text/event-stream"),
320-
HTTPStatus.NOT_ACCEPTABLE,
321-
)
322-
await response(scope, receive, send)
337+
# Validate Accept header
338+
if not await self._validate_accept_header(request, scope, send):
323339
return
324340

325341
# Validate Content-Type

tests/shared/test_streamable_http.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,51 @@ def test_json_response(json_response_server: None, json_server_url: str):
693693
assert response.headers.get("Content-Type") == "application/json"
694694

695695

696+
def test_json_response_accept_json_only(json_response_server: None, json_server_url: str):
697+
"""Test that json_response servers only require application/json in Accept header."""
698+
mcp_url = f"{json_server_url}/mcp"
699+
response = requests.post(
700+
mcp_url,
701+
headers={
702+
"Accept": "application/json",
703+
"Content-Type": "application/json",
704+
},
705+
json=INIT_REQUEST,
706+
)
707+
assert response.status_code == 200
708+
assert response.headers.get("Content-Type") == "application/json"
709+
710+
711+
def test_json_response_missing_accept_header(json_response_server: None, json_server_url: str):
712+
"""Test that json_response servers reject requests without Accept header."""
713+
mcp_url = f"{json_server_url}/mcp"
714+
response = requests.post(
715+
mcp_url,
716+
headers={
717+
"Content-Type": "application/json",
718+
},
719+
json=INIT_REQUEST,
720+
)
721+
assert response.status_code == 406
722+
assert "Not Acceptable" in response.text
723+
724+
725+
def test_json_response_incorrect_accept_header(json_response_server: None, json_server_url: str):
726+
"""Test that json_response servers reject requests with incorrect Accept header."""
727+
mcp_url = f"{json_server_url}/mcp"
728+
# Test with only text/event-stream (wrong for JSON server)
729+
response = requests.post(
730+
mcp_url,
731+
headers={
732+
"Accept": "text/event-stream",
733+
"Content-Type": "application/json",
734+
},
735+
json=INIT_REQUEST,
736+
)
737+
assert response.status_code == 406
738+
assert "Not Acceptable" in response.text
739+
740+
696741
def test_get_sse_stream(basic_server: None, basic_server_url: str):
697742
"""Test establishing an SSE stream via GET request."""
698743
# First, we need to initialize a session

0 commit comments

Comments
 (0)