Skip to content

fix: send notifications/cancelled on request timeout and cancellation#2518

Open
weiguangli-io wants to merge 1 commit intomodelcontextprotocol:mainfrom
weiguangli-io:fix/cancelled-notification-2507
Open

fix: send notifications/cancelled on request timeout and cancellation#2518
weiguangli-io wants to merge 1 commit intomodelcontextprotocol:mainfrom
weiguangli-io:fix/cancelled-notification-2507

Conversation

@weiguangli-io
Copy link
Copy Markdown

Fixes #2507

Root Cause

BaseSession.send_request() never emits a notifications/cancelled message when its in-flight await is interrupted:

  • Timeout (anyio.fail_after): raises MCPError without notifying the server
  • External cancellation (CancelledError): propagates without notifying the server

The server never learns the request was abandoned, so server-side coroutines remain suspended holding resources (DB connections, cursors, locks) until the session ends.

Fix

  • Add a _send_cancelled_notification helper on BaseSession that does best-effort delivery (catches Exception to avoid masking the original error if the transport is already closed)
  • In the TimeoutError handler, send the notification before raising MCPError
  • Add an except anyio.get_cancelled_exc_class() handler with anyio.CancelScope(shield=True) to ensure the notification is sent even during cancellation teardown, then re-raise

CancelledNotification is already part of both ClientNotification and ServerNotification unions, so this works for both client and server sessions.

Fixes modelcontextprotocol#2507. BaseSession.send_request() never emits a
notifications/cancelled message when its in-flight await is interrupted,
whether by the SDK's own timeout or by external cancellation. The server
never learns the request was abandoned, leaving coroutines suspended
holding resources until the session ends.

Add a _send_cancelled_notification helper that does best-effort delivery,
and call it from both the TimeoutError and CancelledError handlers. The
cancellation path uses anyio.CancelScope(shield=True) to ensure the
notification is sent even during teardown.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ClientSession never sends notifications/cancelled when call_tool is cancelled — server-side coroutines leak

1 participant