Skip to content

Commit 898fc88

Browse files
author
chiliu
committed
add unit test for Streamable HTTP Trailing Slash Compatibility
1 parent 0d05075 commit 898fc88

File tree

1 file changed

+119
-115
lines changed

1 file changed

+119
-115
lines changed

tests/shared/test_streamable_http.py

Lines changed: 119 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -598,137 +598,141 @@ def test_session_termination(basic_server, basic_server_url):
598598

599599
def test_response(basic_server, basic_server_url):
600600
"""Test response handling for a valid request."""
601-
mcp_url = f"{basic_server_url}/mcp"
602-
response = requests.post(
603-
mcp_url,
604-
headers={
605-
"Accept": "application/json, text/event-stream",
606-
"Content-Type": "application/json",
607-
},
608-
json=INIT_REQUEST,
609-
)
610-
assert response.status_code == 200
611-
612-
# Now terminate the session
613-
session_id = response.headers.get(MCP_SESSION_ID_HEADER)
614-
615-
# Try to use the terminated session
616-
tools_response = requests.post(
617-
mcp_url,
618-
headers={
619-
"Accept": "application/json, text/event-stream",
620-
"Content-Type": "application/json",
621-
MCP_SESSION_ID_HEADER: session_id, # Use the session ID we got earlier
622-
},
623-
json={"jsonrpc": "2.0", "method": "tools/list", "id": "tools-1"},
624-
stream=True,
625-
)
626-
assert tools_response.status_code == 200
627-
assert tools_response.headers.get("Content-Type") == "text/event-stream"
601+
mcp_urls = [f"{basic_server_url}/mcp", f"{basic_server_url}/mcp/"]
602+
for mcp_url in mcp_urls:
603+
response = requests.post(
604+
mcp_url,
605+
headers={
606+
"Accept": "application/json, text/event-stream",
607+
"Content-Type": "application/json",
608+
},
609+
json=INIT_REQUEST,
610+
)
611+
assert response.status_code == 200
612+
613+
# Now terminate the session
614+
session_id = response.headers.get(MCP_SESSION_ID_HEADER)
615+
616+
# Try to use the terminated session
617+
tools_response = requests.post(
618+
mcp_url,
619+
headers={
620+
"Accept": "application/json, text/event-stream",
621+
"Content-Type": "application/json",
622+
MCP_SESSION_ID_HEADER: session_id, # Use the session ID we got earlier
623+
},
624+
json={"jsonrpc": "2.0", "method": "tools/list", "id": "tools-1"},
625+
stream=True,
626+
)
627+
assert tools_response.status_code == 200
628+
assert tools_response.headers.get("Content-Type") == "text/event-stream"
628629

629630

630631
def test_json_response(json_response_server, json_server_url):
631632
"""Test response handling when is_json_response_enabled is True."""
632-
mcp_url = f"{json_server_url}/mcp"
633-
response = requests.post(
634-
mcp_url,
635-
headers={
636-
"Accept": "application/json, text/event-stream",
637-
"Content-Type": "application/json",
638-
},
639-
json=INIT_REQUEST,
640-
)
641-
assert response.status_code == 200
642-
assert response.headers.get("Content-Type") == "application/json"
633+
mcp_urls = [f"{basic_server_url}/mcp", f"{basic_server_url}/mcp/"]
634+
for mcp_url in mcp_urls:
635+
response = requests.post(
636+
mcp_url,
637+
headers={
638+
"Accept": "application/json, text/event-stream",
639+
"Content-Type": "application/json",
640+
},
641+
json=INIT_REQUEST,
642+
)
643+
assert response.status_code == 200
644+
assert response.headers.get("Content-Type") == "application/json"
643645

644646

645647
def test_get_sse_stream(basic_server, basic_server_url):
646648
"""Test establishing an SSE stream via GET request."""
647649
# First, we need to initialize a session
648-
mcp_url = f"{basic_server_url}/mcp"
649-
init_response = requests.post(
650-
mcp_url,
651-
headers={
652-
"Accept": "application/json, text/event-stream",
653-
"Content-Type": "application/json",
654-
},
655-
json=INIT_REQUEST,
656-
)
657-
assert init_response.status_code == 200
658-
659-
# Get the session ID
660-
session_id = init_response.headers.get(MCP_SESSION_ID_HEADER)
661-
assert session_id is not None
662-
663-
# Now attempt to establish an SSE stream via GET
664-
get_response = requests.get(
665-
mcp_url,
666-
headers={
667-
"Accept": "text/event-stream",
668-
MCP_SESSION_ID_HEADER: session_id,
669-
},
670-
stream=True,
671-
)
672-
673-
# Verify we got a successful response with the right content type
674-
assert get_response.status_code == 200
675-
assert get_response.headers.get("Content-Type") == "text/event-stream"
650+
mcp_urls = [f"{basic_server_url}/mcp", f"{basic_server_url}/mcp/"]
651+
for mcp_url in mcp_urls:
652+
init_response = requests.post(
653+
mcp_url,
654+
headers={
655+
"Accept": "application/json, text/event-stream",
656+
"Content-Type": "application/json",
657+
},
658+
json=INIT_REQUEST,
659+
)
660+
assert init_response.status_code == 200
661+
662+
# Get the session ID
663+
session_id = init_response.headers.get(MCP_SESSION_ID_HEADER)
664+
assert session_id is not None
665+
666+
# Now attempt to establish an SSE stream via GET
667+
get_response = requests.get(
668+
mcp_url,
669+
headers={
670+
"Accept": "text/event-stream",
671+
MCP_SESSION_ID_HEADER: session_id,
672+
},
673+
stream=True,
674+
)
676675

677-
# Test that a second GET request gets rejected (only one stream allowed)
678-
second_get = requests.get(
679-
mcp_url,
680-
headers={
681-
"Accept": "text/event-stream",
682-
MCP_SESSION_ID_HEADER: session_id,
683-
},
684-
stream=True,
685-
)
676+
# Verify we got a successful response with the right content type
677+
assert get_response.status_code == 200
678+
assert get_response.headers.get("Content-Type") == "text/event-stream"
679+
680+
# Test that a second GET request gets rejected (only one stream allowed)
681+
second_get = requests.get(
682+
mcp_url,
683+
headers={
684+
"Accept": "text/event-stream",
685+
MCP_SESSION_ID_HEADER: session_id,
686+
},
687+
stream=True,
688+
)
686689

687-
# Should get CONFLICT (409) since there's already a stream
688-
# Note: This might fail if the first stream fully closed before this runs,
689-
# but generally it should work in the test environment where it runs quickly
690-
assert second_get.status_code == 409
690+
# Should get CONFLICT (409) since there's already a stream
691+
# Note: This might fail if the first stream fully closed before this runs,
692+
# but generally it should work in the test environment where it runs quickly
693+
assert second_get.status_code == 409
691694

692695

693696
def test_get_validation(basic_server, basic_server_url):
694697
"""Test validation for GET requests."""
695698
# First, we need to initialize a session
696-
mcp_url = f"{basic_server_url}/mcp"
697-
init_response = requests.post(
698-
mcp_url,
699-
headers={
700-
"Accept": "application/json, text/event-stream",
701-
"Content-Type": "application/json",
702-
},
703-
json=INIT_REQUEST,
704-
)
705-
assert init_response.status_code == 200
706-
707-
# Get the session ID
708-
session_id = init_response.headers.get(MCP_SESSION_ID_HEADER)
709-
assert session_id is not None
710-
711-
# Test without Accept header
712-
response = requests.get(
713-
mcp_url,
714-
headers={
715-
MCP_SESSION_ID_HEADER: session_id,
716-
},
717-
stream=True,
718-
)
719-
assert response.status_code == 406
720-
assert "Not Acceptable" in response.text
721-
722-
# Test with wrong Accept header
723-
response = requests.get(
724-
mcp_url,
725-
headers={
726-
"Accept": "application/json",
727-
MCP_SESSION_ID_HEADER: session_id,
728-
},
729-
)
730-
assert response.status_code == 406
731-
assert "Not Acceptable" in response.text
699+
mcp_urls = [f"{basic_server_url}/mcp", f"{basic_server_url}/mcp/"]
700+
for mcp_url in mcp_urls:
701+
init_response = requests.post(
702+
mcp_url,
703+
headers={
704+
"Accept": "application/json, text/event-stream",
705+
"Content-Type": "application/json",
706+
},
707+
json=INIT_REQUEST,
708+
)
709+
assert init_response.status_code == 200
710+
711+
# Get the session ID
712+
session_id = init_response.headers.get(MCP_SESSION_ID_HEADER)
713+
assert session_id is not None
714+
715+
# Test without Accept header
716+
response = requests.get(
717+
mcp_url,
718+
headers={
719+
MCP_SESSION_ID_HEADER: session_id,
720+
},
721+
stream=True,
722+
)
723+
assert response.status_code == 406
724+
assert "Not Acceptable" in response.text
725+
726+
# Test with wrong Accept header
727+
response = requests.get(
728+
mcp_url,
729+
headers={
730+
"Accept": "application/json",
731+
MCP_SESSION_ID_HEADER: session_id,
732+
},
733+
)
734+
assert response.status_code == 406
735+
assert "Not Acceptable" in response.text
732736

733737

734738
# Client-specific fixtures

0 commit comments

Comments
 (0)