feat(session): propagate callback exceptions to the awaiter#2674
feat(session): propagate callback exceptions to the awaiter#2674Ar-maan05 wants to merge 13 commits into
Conversation
- Guard stream.aclose() in propagate handler with try/except to handle already-closed streams (mirrors existing connection-close cleanup pattern) - Include exception class name and message in the INTERNAL_ERROR response sent to the peer for better debuggability - Add explanatory comment on why the receive loop exits after propagation
…r error msg" This reverts commit ed5721c.
d511614 to
0de6e40
Compare
|
Merged the latest The only collision was a one-line coverage pragma in Verified locally: |
|
Friendly ping on this one; CI is green (27/27) and it's rebased on latest |
Allow exceptions raised inside user-defined client callbacks (elicitation, sampling, list roots) to propagate back to the awaiter of the outgoing request (e.g.,
session.call_tool), instead of being silently swallowed by the receive loop and converted into a genericINVALID_PARAMSJSON-RPC error.Usage Example
Users opt-in to propagation by adding a
__mcp_propagate__marker attribute to their custom exceptions:Solution
• Added self._propagate_errors: dict[RequestId, BaseException] = {} to BaseSession to stash exceptions marked with mcp_propagate = True .
• In _receive_loop , if a callback exception has mcp_propagate = True :
• Send an INTERNAL_ERROR response back to the server so it doesn't hang.
• Populate _propagate_errors for active request IDs.
• Close active outgoing request streams and exit the receive loop.
• In send_request , catch anyio.EndOfStream and raise the stashed exception on the caller's task.
• Added test_callback_exception_propagation in tests/shared/test_session.py to prevent regressions.
Closes #2673