|
30 | 30 | from mcp.shared._compat import resync_tracer |
31 | 31 | from mcp.shared.dispatcher import ( |
32 | 32 | CallOptions, |
| 33 | + DispatchContext, |
33 | 34 | OnNotify, |
34 | 35 | OnNotifyIntercept, |
35 | 36 | OnRequest, |
|
43 | 44 |
|
44 | 45 | logger = logging.getLogger(__name__) |
45 | 46 |
|
| 47 | + |
| 48 | +def _shielded_progress(fn: ProgressFnT) -> ProgressFnT: |
| 49 | + """Wrap a progress callback so its failure does not fail the request.""" |
| 50 | + |
| 51 | + async def _wrapped(progress: float, total: float | None, message: str | None) -> None: |
| 52 | + try: |
| 53 | + await fn(progress, total, message) |
| 54 | + except Exception: |
| 55 | + logger.exception("progress callback raised") |
| 56 | + |
| 57 | + return _wrapped |
| 58 | + |
| 59 | + |
| 60 | +def _contained_notify(fn: OnNotify) -> OnNotify: |
| 61 | + """Wrap a notification handler so its failure does not reach the sender.""" |
| 62 | + |
| 63 | + async def _wrapped(dctx: DispatchContext[TransportContext], method: str, params: Mapping[str, Any] | None) -> None: |
| 64 | + try: |
| 65 | + await fn(dctx, method, params) |
| 66 | + except Exception: |
| 67 | + logger.exception("notification handler for %r raised", method) |
| 68 | + |
| 69 | + return _wrapped |
| 70 | + |
46 | 71 | __all__ = ["DirectDispatcher", "create_direct_dispatcher_pair"] |
47 | 72 |
|
48 | 73 | DIRECT_TRANSPORT_KIND = "direct" |
@@ -206,7 +231,7 @@ def _make_context( |
206 | 231 | _back_request=lambda m, p, o: peer._dispatch_request(m, p, o), |
207 | 232 | _back_notify=lambda m, p: peer._dispatch_notify(m, p), |
208 | 233 | request_id=request_id, |
209 | | - _on_progress=on_progress, |
| 234 | + _on_progress=_shielded_progress(on_progress) if on_progress is not None else None, |
210 | 235 | ) |
211 | 236 |
|
212 | 237 | async def _wait_ready(self) -> None: |
@@ -301,7 +326,7 @@ async def _dispatch_notify(self, method: str, params: Mapping[str, Any] | None) |
301 | 326 | return |
302 | 327 | assert self._on_notify is not None |
303 | 328 | dctx = self._make_context() |
304 | | - await self._on_notify(dctx, method, params) |
| 329 | + await _contained_notify(self._on_notify)(dctx, method, params) |
305 | 330 |
|
306 | 331 |
|
307 | 332 | def create_direct_dispatcher_pair( |
|
0 commit comments