Skip to content

.NET: [Bug]: ChatClientAgent + dynamic ChatOptions.Tools: middleware ordering vs plugin-time refresh; FunctionInvokingChatClient wrapping and IncludeDetailedErrors #5326

@torsilver

Description

@torsilver

Description

Summary

We use Microsoft.Agents.AI (1.0.0) with ChatClientAgent, ChatClientAgentRunOptions, and custom function-calling middleware (agent.AsBuilder().Use(...)). Our product uses dynamic tooling: we only send a bootstrap tool subset to the model first; the model calls activate_tools to add more tools in the same streaming run.

We had to add application-side workarounds that are more about orchestration and timing than raw MEAI types. We’d like clearer first-class guidance or hooks from the Agent Framework so “tools change mid-run” is a supported story.

Note: ChatOptions.Tools snapshot / Clone behavior is also discussed under Microsoft.Extensions.AI (dotnet/extensions #7217, #7218). This issue focuses on MAF-specific integration points.

Environment

Microsoft.Agents.AI / Microsoft.Agents.AI.OpenAI / Microsoft.Agents.AI.Workflows: 1.0.0

Microsoft.Extensions.AI: 10.5.x

Entry: ChatClientAgent + RunStreamingAsync, tools from ChatOptions bound per pass.

  1. Refreshing ChatOptions.Tools after activate_tools: middleware path was unreliable

Observed: We tried to refresh the effective tool list in function-calling middleware after the activate_tools tool returned. In practice this was not reliable (e.g. ordering, or not every internal path hitting the same middleware completion semantics). The model could still see Function not found for a tool that had just been activated.

Our workaround: We refresh the in-place list bound to the current pass inside the plugin that implements activate_tools (immediately after successful activation), rebuilding the List that backs ChatOptions.Tools for that pass. Comment in code: avoid relying only on MAF middleware order.

Ask:

Document when middleware runs relative to tool execution and the next model request, and whether mutating ChatOptions.Tools from middleware is a supported pattern.

If not, provide a documented hook (e.g. “after tool X returns, recompute tools”) or a callback registered on the agent/session so the framework merges updated tools before the next LLM call.

  1. ChatClientAgent and FunctionInvokingChatClient / IncludeDetailedErrors

Observed: We need IncludeDetailedErrors = true on FunctionInvokingChatClient so tool failures surface consistently for our UI and logging.

Our workaround: We explicitly wrap the inner IChatClient with FunctionInvokingChatClient, because ChatClientAgent only adds FunctionInvokingChatClient when the inner client does not already contain one (per our reading of behavior). We need predictable error detail regardless of inner wrapping.

Ask: Expose IncludeDetailedErrors (or equivalent) on ChatClientAgentOptions / agent builder so hosts don’t depend on whether the inner pipeline already includes FunctionInvokingChatClient.

  1. Streaming tool-call deltas

We implemented a small extractor from AgentResponseUpdate to our own stream protocol (call id, incremental JSON args), similar to what we had for a previous stack.

Ask: Point to official recommended patterns/samples for consuming streaming tool call deltas with ChatClientAgent, to reduce duplicated glue code across products.

  1. Optional: AgentGroupChat / multi-executor paths vs dynamic tools

We documented a potential mismatch: when UseAgentGroupChat-style main session is enabled, worker tool lists may not receive the same ChatOptions/dynamic tool state as the single-agent main path (e.g. ToolsForAgentRound / mutation target). We have not fully aligned those code paths.

Ask: If Group Chat + variable tool sets is a supported combination, please document how to pass the same tool options to all executors.

Code Sample

Error Messages / Stack Traces

Package Versions

Microsoft.Agents.AI / Microsoft.Agents.AI.OpenAI / Microsoft.Agents.AI.Workflows: 1.0.0

.NET Version

net10.0

Additional Context

No response

Metadata

Metadata

Assignees

Labels

Type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions