Skip to content

Commit f9598ad

Browse files
committed
attempt to support cancelation
1 parent 58c5e72 commit f9598ad

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

src/mcp/shared/session.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from mcp.shared.message import MessageMetadata, ServerMessageMetadata, SessionMessage
1616
from mcp.types import (
1717
CancelledNotification,
18+
CancelledNotificationParams,
1819
ClientNotification,
1920
ClientRequest,
2021
ClientResult,
@@ -33,6 +34,7 @@
3334
SendRequestT = TypeVar("SendRequestT", ClientRequest, ServerRequest)
3435
SendResultT = TypeVar("SendResultT", ClientResult, ServerResult)
3536
SendNotificationT = TypeVar("SendNotificationT", ClientNotification, ServerNotification)
37+
SendNotificationInternalT = TypeVar("SendNotificationInternalT", CancelledNotification, ClientNotification, ServerNotification)
3638
ReceiveRequestT = TypeVar("ReceiveRequestT", ClientRequest, ServerRequest)
3739
ReceiveResultT = TypeVar("ReceiveResultT", bound=BaseModel)
3840
ReceiveNotificationT = TypeVar(
@@ -254,6 +256,8 @@ async def send_request(
254256
elif self._session_read_timeout_seconds is not None:
255257
timeout = self._session_read_timeout_seconds.total_seconds()
256258

259+
response_or_error = None
260+
257261
try:
258262
with anyio.fail_after(timeout):
259263
response_or_error = await response_stream_reader.receive()
@@ -268,7 +272,21 @@ async def send_request(
268272
),
269273
)
270274
)
275+
except anyio.get_cancelled_exc_class():
276+
with anyio.CancelScope(shield=True):
277+
notification = CancelledNotification(
278+
method="notifications/cancelled",
279+
params=CancelledNotificationParams(
280+
requestId=request_id,
281+
reason="cancelled"
282+
)
283+
)
284+
await self._send_notification_internal(notification, request_id, )
271285

286+
if response_or_error is None:
287+
raise McpError(
288+
ErrorData(code=32601, message="request cancelled")
289+
)
272290
if isinstance(response_or_error, JSONRPCError):
273291
raise McpError(response_or_error.error)
274292
else:
@@ -283,6 +301,13 @@ async def send_notification(
283301
self,
284302
notification: SendNotificationT,
285303
related_request_id: RequestId | None = None,
304+
) -> None:
305+
await self._send_notification_internal(notification, related_request_id)
306+
307+
async def _send_notification_internal(
308+
self,
309+
notification: SendNotificationInternalT,
310+
related_request_id: RequestId | None = None,
286311
) -> None:
287312
"""
288313
Emits a notification, which is a one-way message that does not expect

tests/shared/test_streamable_http.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ async def test_streamablehttp_client_session_termination(
989989
# Attempt to make a request after termination
990990
with pytest.raises(
991991
McpError,
992-
match="Session terminated",
992+
match="request cancelled",
993993
):
994994
await session.list_tools()
995995

0 commit comments

Comments
 (0)