Repository navigation
Description
Description
When using GetAIAgentAsync without providing the tools list, the tools are being map in tool map using AsAITool, which instead of getting the real function anem and description OpenAIResponsesChatClient.ResponseToolAITool(tool), which uses Name => Tool.GetType().Name this means that the tool will always have the worng name in the map and will never be invocated. The reason I need this is beacause I am trying to resolve the function calls my self, but function invocation never reaches the FunctionInvoker as toolMap.TryGetValue is always false. The correct information remain inside AITool.Tool
The main problem is that there is no way to manually resolve the tool calls without always providing their method in tools array, so even manually handlem is not an options as middleware also does not get it
Code Sample
IChatClient GetChatClient(IChatClient chatClient)
{
var functionClient = new FunctionInvokingChatClient(chatClient);
functionClient.FunctionInvoker = FunctionInvoker;
functionClient.TerminateOnUnknownCalls = false;
return functionClient;
}
ValueTask<object?> FunctionInvoker(FunctionInvocationContext context, CancellationToken token)
{
if (context.Function.Name == "GetWeather")
{
return new ValueTask<object?>(
$"The weather in {context.Arguments.GetValueOrDefault("location")} is cloudy with a high of 15°C.");
}
return new ValueTask<object?>(DateTimeOffset.UtcNow);
}
var modifiedAgent =
await aiProjectClient.GetAIAgentAsync("TestKS", clientFactory: GetChatClient);Error Messages / Stack Traces
Error: Requested function "GetWeather" not found.Package Versions
Azure.AI.Projects 1.2.0-beta.5; Microsoft.Agents.AI.AzureAI 1.0.0-preview.260205.1
.NET Version
.NET10.0
Additional Context
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
[-].NET: [Bug]: Toll calls do not work without providing them in tools array[/-][+].NET: [Bug]: Tool calls do not work without providing them in tools array[/+]KornelijusS commentedon Feb 11, 2026
Or do I understand everything wrong and ChatClientAgent does not have the possibility to call non-invocable functions? As I found that you can do it using ResponsesClient, but I want to use Project Conversation and to not have to manually add previousResponseId. The only workaround I found now is to create and agent with read methods and then later get it but use a fake method and intercept with middle ware or FunctionInvocator. To sum up, is there currently a way to manually resolve function calls in ChatClientAgent? if so how?
Skip tool validation when UseProvidedChatClientAsIs is true (microsof…
rogerbarreto commentedon Mar 2, 2026
Hi KornelijusS, thanks for reporting this! Here is some context on what's happening and workarounds.
Why
FunctionInvokeris never calledWhen no tools are provided in the
toolsarray, the server-side tool definitions are wrapped viaresponseTool.AsAITool()fromMicrosoft.Extensions.AI.OpenAI. This creates aResponseToolAIToolwrapper that:AITool(notAIFunctionDeclarationorAIFunction)Nameset toGetType().Name→"FunctionTool"instead of"GetWeather"The
FunctionInvokingChatClient(FICC) can only invoke tools that areAIFunctioninstances. It finds tools by checkingtool is AIFunctionDeclaration && tool.Name == requestedName. SinceResponseToolAIToolis justAITool, FICC skips it entirely and returns"Requested function 'GetWeather' not found". YourFunctionInvokeris never reached.Intended design: Provide invocable
AIFunctionmatching the server-side tool definitionsDeclarative server-side agents with function tools are expected to have invocable function pointers provided at runtime. When calling
GetAIAgentAsync, you should map each declarative function tool from the agent definition to a localAIFunctionimplementation and pass them in thetoolsarray:If you have many declarative functions and want to intercept them all with a custom handler, you can either build a local mapping from the agent's declarative definitions into invocable
AIFunctioninstances before callingGetAIAgentAsync, or handle function calls manually via a middleware as shown below.Alternative: Handle function calls manually via ChatClient middleware
If you want full control over function call resolution without relying on FICC at all, you can use a
DelegatingChatClientmiddleware that interceptsFunctionCallContentin the LLM response and producesFunctionResultContentresults manually.Important: When bypassing FICC this way, you are responsible for the full function call lifecycle — detecting calls, resolving them, and sending results back to the LLM. A
DelegatingChatClientmiddleware is the idiomatic way to do this in the MEAI pipeline.You also need a
NoOpFunctionInvokingChatClient— this extendsFunctionInvokingChatClientso thatGetService<FunctionInvokingChatClient>()returns it (preventingWithDefaultAgentMiddlewarefrom adding a real FICC that would fail), but it passes all calls straight through without any function invocation logic:Upcoming fix
PR #4389 makes the
GetAIAgentAsync(ChatClientAgentOptions)overload respectUseProvidedChatClientAsIs = trueto skip tool validation. Once that fix ships, you'll no longer need theNoOpFunctionInvokingChatClientworkaround — just theManualToolHandlingMiddlewarewithUseProvidedChatClientAsIs = truewill be sufficient.Does either workaround unblock you. Let us know if you have questions.
Decouple tool matching from validation and add tool preservation test (…
.NET: AzureAI Package - Skip tool validation when UseProvidedChatClie…