Add ToolProgressIndicator for real-time tool progress updates - #1393
Conversation
Introduced ToolProgressIndicator to relay progress updates from MCP tool executions to the chat interface. Progress is pushed via MessageHub during long-running tool calls, improving user feedback for tools that report incremental progress.
PR Summary by QodoRelay MCP tool progress to chat via ToolProgressIndicator
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1. Observer exceptions break tool
|
| _hub.Push(new() | ||
| { | ||
| EventName = ChatEvent.OnIndicationReceived, | ||
| Data = indication, |
There was a problem hiding this comment.
1. Observer exceptions break tool 🐞 Bug ☼ Reliability
ToolProgressIndicator.Report pushes progress into MessageHub without guarding against subscriber/listener exceptions; failures in the indication pipeline (e.g., SSE writes when a client disconnects) can propagate back into the MCP progress callback and cause CallToolAsync to fault/abort. This makes tool execution reliability depend on UI/observer health rather than being best-effort telemetry.
Agent Prompt
### Issue description
`ToolProgressIndicator.Report` calls `_hub.Push(...)` inline. Because `MessageHub.Push` directly calls `OnNext` on a synchronized `Subject`, any exception thrown by observers/listeners can bubble back through `Push` into the MCP SDK's progress callback, potentially failing the tool call.
### Issue Context
Indication listeners include SSE handlers that write to `HttpResponse.Body`. Those writes can throw (e.g., client disconnect), and `ConversationObserver` executes listeners synchronously (`GetResult()`), so the exception can propagate to the progress callback thread.
### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Routing/Executor/MCPToolExecutor.cs[134-154]
### What to change
- Wrap `_hub.Push(...)` in a `try/catch` inside `Report`.
- On exception, swallow (best-effort) and log via an injected `ILogger` (either pass one into `ToolProgressIndicator` via `For(...)`, or resolve it in `For(...)` and store it).
- Ensure the tool execution result is not impacted by notification delivery failures.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| /// Deliberately synchronous, which is why this is a hand-written IProgress rather than a | ||
| /// <see cref="Progress{T}"/>: Progress<T> queues every report to the thread pool | ||
| /// independently, so two steps reported together can arrive out of order — and the one | ||
| /// that arrives last is the one left on screen. Pushing inline keeps the order the server |
There was a problem hiding this comment.
2. Progress blocks tool io 🐞 Bug ➹ Performance
ToolProgressIndicator.Report is deliberately synchronous and triggers the full indication delivery path inline, so frequent progress notifications can block the MCP notification/transport thread and delay tool completion/response processing. In the SSE path, each indication can synchronously wait on network I/O and an explicit Task.Delay(10), amplifying latency under many progress steps or slow clients.
Agent Prompt
### Issue description
The progress callback runs synchronously and pushes indications through `MessageHub`, which invokes observers inline. Downstream observers synchronously wait for async listeners (including SSE writes), so the MCP progress callback thread can be blocked by network I/O and artificial delays.
### Issue Context
The implementation intentionally avoided `Progress<T>` to preserve ordering. You can preserve ordering without blocking the MCP callback thread by using a single-threaded ordered queue/Channel consumer.
### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Routing/Executor/MCPToolExecutor.cs[124-154]
### What to change
- Replace direct `_hub.Push(...)` inside `Report` with an ordered async dispatcher:
- Use `Channel<ProgressNotificationValue>` (bounded) or an `ActionBlock`/queue.
- `Report(...)` should enqueue quickly (non-blocking) and return.
- A single background consumer (per tool call) should read in order and push to the hub.
- Consider coalescing/throttling repeated messages to reduce UI spam.
- Keep best-effort semantics (drop on backpressure rather than blocking the transport thread).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
No description provided.