Busy stays True forever after any request that involves a tool call (function calling), because FBusyCount is incremented and decremented on different object instances.
Details:
TTMSMCPCloudAI.ExecuteHistory does Inc(FBusyCount) on the instance it's called on. The matching dec(FBusyCount) happens in DoExecuted, but only when not lToolReq.
When a provider response contains a tool-use part (e.g. in DoClaudeRequest, but the same pattern exists for the other providers), the follow-up request is executed on a newly created, separate TTMSMCPCloudAI instance instead of Self:
if lToolUse then
begin
lToolReq := true;
...
FToolsRequest := TTMSMCPCloudAI.Create;
FToolsRequest.Assign(Self);
FToolsRequest.OnExecuted := OnExecuted;
FToolsRequest.ExecuteHistory(ARequestResult.Name); // Inc(FBusyCount) runs on FToolsRequest, not on Self
FToolsResponse := '';
end;
...
if not lToolReq then
DoExecuted(...); // skipped when a tool was called -> Self.FBusyCount is never decremented
Self.FBusyCount was incremented by the original ExecuteHistory call, but Self.DoExecuted is now never invoked because lToolReq = True, so the matching dec(FBusyCount) never happens on Self. FToolsRequest manages its own, independent FBusyCount, which balances out fine on its own instance — but that doesn't help Self. Each further nesting level (a tool response that itself triggers another tool call) leaks another increment the same way.
Impact: Any application that reads Busy to gate UI (e.g. disable "Send"/enable "Cancel" while a request is in flight) gets permanently stuck in "busy" state after the first request that uses function calling, even though the conversation completes normally and OnExecuted fires correctly (since OnExecuted is forwarded down through FToolsRequest.OnExecuted := OnExecuted).
Steps to reproduce:
- Configure
TTMSMCPCloudAIwith at least one tool viaTools.Add. - Send a message that causes the model to call that tool.
- Check
BusyafterOnExecutedhas fired for the final response — it remainsTrue.
Expected: Busy returns to False once the full conversation turn (including any tool-call round trips) has completed.
Actual: Busy stays True permanently after the first tool call, because the busy-count bookkeeping is split across separate TTMSMCPCloudAI instances that don't share FBusyCount.
Suggested fix: Track the busy count on the top-level instance (e.g. pass it through to FToolsRequest or increment/decrement a shared counter), or call Self.DoExecuted-equivalent bookkeeping regardless of lToolReq, so the outer instance's FBusyCount is always balanced once the whole tool-call chain finishes.