11from collections .abc import AsyncGenerator
22from typing import Any
3+ from unittest .mock import AsyncMock , MagicMock
34
45import anyio
56import pytest
1011from mcp .shared .exceptions import McpError
1112from mcp .shared .memory import create_client_server_memory_streams , create_connected_server_and_client_session
1213from mcp .shared .message import SessionMessage
14+ from mcp .shared .session import RequestResponder
1315from mcp .types import (
1416 CancelledNotification ,
1517 CancelledNotificationParams ,
1618 ClientNotification ,
1719 ClientRequest ,
20+ ClientResult ,
1821 EmptyResult ,
1922 ErrorData ,
2023 JSONRPCError ,
@@ -30,6 +33,20 @@ def mcp_server() -> Server:
3033 return Server (name = "test server" )
3134
3235
36+ def make_request_responder () -> tuple [RequestResponder [ClientRequest , ClientResult ], MagicMock ]:
37+ mock_session = MagicMock ()
38+ mock_session ._send_response = AsyncMock ()
39+ request = ClientRequest (types .PingRequest ())
40+ responder : RequestResponder [ClientRequest , ClientResult ] = RequestResponder (
41+ request_id = 1 ,
42+ request_meta = None ,
43+ request = request ,
44+ session = mock_session ,
45+ on_complete = lambda responder : None ,
46+ )
47+ return responder , mock_session
48+
49+
3350@pytest .fixture
3451async def client_connected_to_server (
3552 mcp_server : Server ,
@@ -128,6 +145,33 @@ async def make_request(client_session: ClientSession):
128145 await ev_cancelled .wait ()
129146
130147
148+ @pytest .mark .anyio
149+ async def test_request_responder_respond_after_cancel_does_not_raise ():
150+ responder , mock_session = make_request_responder ()
151+
152+ with responder :
153+ await responder .cancel ()
154+ await responder .respond (ClientResult (root = EmptyResult ()))
155+
156+ mock_session ._send_response .assert_awaited_once ()
157+ assert mock_session ._send_response .await_args .kwargs ["response" ] == ErrorData (
158+ code = 0 , message = "Request cancelled" , data = None
159+ )
160+
161+
162+ @pytest .mark .anyio
163+ async def test_request_responder_cancel_after_respond_does_not_send_error ():
164+ responder , mock_session = make_request_responder ()
165+ response = ClientResult (root = EmptyResult ())
166+
167+ with responder :
168+ await responder .respond (response )
169+ await responder .cancel ()
170+
171+ mock_session ._send_response .assert_awaited_once ()
172+ assert mock_session ._send_response .await_args .kwargs ["response" ] == response
173+
174+
131175@pytest .mark .anyio
132176async def test_response_id_type_mismatch_string_to_int ():
133177 """
0 commit comments