|
13 | 13 |
|
14 | 14 | import pytest |
15 | 15 | import uvicorn |
| 16 | +from mcp.shared.session import RequestResponder |
16 | 17 | from starlette.applications import Starlette |
17 | 18 | from starlette.requests import Request |
18 | 19 | from starlette.responses import JSONResponse, Response |
19 | 20 | from starlette.routing import Route |
20 | 21 |
|
21 | | -from mcp.client.session import ClientSession |
| 22 | +from mcp import ClientSession, types |
22 | 23 | from mcp.client.streamable_http import streamablehttp_client |
23 | 24 | from mcp.types import ClientNotification, Implementation, RootsListChangedNotification |
24 | 25 |
|
@@ -50,7 +51,10 @@ async def handle_mcp_request(request: Request) -> Response: |
50 | 51 |
|
51 | 52 | # For notifications, return 204 No Content (non-SDK behavior) |
52 | 53 | if "id" not in data: |
53 | | - return Response(status_code=204) |
| 54 | + return Response( |
| 55 | + status_code=204, |
| 56 | + headers={"Content-Type": "application/json"} |
| 57 | + ) |
54 | 58 |
|
55 | 59 | # Default response for other requests |
56 | 60 | return JSONResponse({ |
@@ -140,30 +144,33 @@ async def test_notification_with_204_response( |
140 | 144 | the response body. |
141 | 145 | """ |
142 | 146 | server_url = f"http://127.0.0.1:{non_sdk_server_port}/mcp" |
143 | | - |
| 147 | + returned_exception = None |
| 148 | + async def message_handler(message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception): |
| 149 | + nonlocal returned_exception |
| 150 | + if isinstance(message, Exception): |
| 151 | + returned_exception = message |
| 152 | + |
144 | 153 | async with streamablehttp_client(server_url) as (read_stream, write_stream, get_session_id): |
145 | 154 | async with ClientSession( |
146 | 155 | read_stream, |
147 | 156 | write_stream, |
148 | | - client_info=Implementation(name="test-client", version="1.0.0") |
| 157 | + message_handler=message_handler, |
149 | 158 | ) as session: |
150 | 159 | # Initialize should work normally |
151 | 160 | await session.initialize() |
152 | 161 |
|
153 | 162 | # Send a notification - this should not raise an error |
154 | 163 | # even though the server returns 204 instead of 202 |
155 | | - notification_sent = False |
156 | | - try: |
157 | | - await session.send_notification( |
158 | | - ClientNotification( |
159 | | - RootsListChangedNotification( |
160 | | - method="notifications/roots/list_changed", |
161 | | - params={} |
162 | | - ) |
| 164 | + # Without the fix, this would fail with a JSON parsing error |
| 165 | + # because the client would try to parse the empty 204 response body |
| 166 | + await session.send_notification( |
| 167 | + ClientNotification( |
| 168 | + RootsListChangedNotification( |
| 169 | + method="notifications/roots/list_changed", |
| 170 | + params={} |
163 | 171 | ) |
164 | 172 | ) |
165 | | - notification_sent = True |
166 | | - except Exception as e: |
167 | | - pytest.fail(f"Notification failed with 204 response: {e}") |
168 | | - |
169 | | - assert notification_sent, "Notification should have been sent successfully" |
| 173 | + ) |
| 174 | + |
| 175 | + if returned_exception: |
| 176 | + pytest.fail(f"Server encountered an exception: {returned_exception}") |
0 commit comments