Microsoft Agent Framework
Microsoft.Extensions.AI
Created: 27 Feb 2026
Updated: 27 Feb 2026
IChatClient: Tool Calling
Tool calling (also called function calling) lets the model invoke .NET methods on your behalf when it needs real data. Instead of guessing an answer, the model emits a function call request. The framework executes the method, sends the result back to the model, and the model produces a final reply.
Key Concepts
1. AIFunctionFactory.Create
Wrap any .NET method as an AIFunction that the model can call. The optional name and description help the model decide when to use it:
AIFunction priceFunc = AIFunctionFactory.Create(
GetBookPrice,
"GetBookPrice",
"Returns the price of a book by title");
2. FunctionInvokingChatClient
Add automatic function invocation to the pipeline using ChatClientBuilder. When the model requests a tool call, this layer executes the function and sends the result back automatically — your outer loop just sees the final answer:
IChatClient client = new ChatClientBuilder(
new OpenAIClient(apiKey).GetChatClient("gpt-4o-mini").AsIChatClient())
.UseFunctionInvocation()
.Build();
3. Registering Tools in ChatOptions
Pass the tools to every request via ChatOptions.Tools:
var options = new ChatOptions
{
Tools =
[
AIFunctionFactory.Create(GetBookPrice, "GetBookPrice", "Returns the price of a book"),
AIFunctionFactory.Create(CheckAvailability, "CheckAvailability", "Returns stock count for a book")
]
};
ChatResponse response = await client.GetResponseAsync("How much does Clean Code cost?", options);
Full Example
using Microsoft.Extensions.AI;
using OpenAI;
namespace MicrosoftAgentFrameworkLesson.ConsoleApp.ChatClient;
/// <summary>
/// Demonstrates IChatClient tool calling with AIFunctionFactory and FunctionInvokingChatClient.
/// Scenario: Bookstore assistant with GetBookPrice and CheckAvailability tools.
/// </summary>
public static class ToolCallingDemo
{
// Simulated bookstore data
private static string GetBookPrice(string title)
{
var prices = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["clean code"] = 35.99m,
["the pragmatic programmer"] = 42.50m,
["design patterns"] = 49.99m,
["refactoring"] = 38.00m
};
return prices.TryGetValue(title, out var price)
? $"${price:N2}"
: "Title not found in catalog.";
}
private static string CheckAvailability(string title)
{
var stock = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["clean code"] = 12,
["the pragmatic programmer"] = 0,
["design patterns"] = 5,
["refactoring"] = 3
};
return stock.TryGetValue(title, out var count)
? count > 0 ? $"{count} copies in stock" : "Out of stock"
: "Title not found in catalog.";
}
public static async Task RunAsync()
{
var apiKey = Environment.GetEnvironmentVariable("OPEN_AI_KEY")
?? throw new InvalidOperationException("Set OPEN_AI_KEY environment variable.");
// Wrap the client with automatic function invocation
IChatClient client = new ChatClientBuilder(
new OpenAIClient(apiKey).GetChatClient("gpt-4o-mini").AsIChatClient())
.UseFunctionInvocation()
.Build();
var options = new ChatOptions
{
Tools =
[
AIFunctionFactory.Create(GetBookPrice, "GetBookPrice", "Returns the price of a book by title"),
AIFunctionFactory.Create(CheckAvailability, "CheckAvailability", "Returns how many copies of a book are in stock")
]
};
Console.WriteLine("====== IChatClient — Tool Calling ======\n");
string[] queries =
[
"How much does Clean Code cost?",
"Is The Pragmatic Programmer available to buy?",
"Tell me the price and availability of Design Patterns."
];
foreach (var query in queries)
{
Console.WriteLine($"Q: {query}");
ChatResponse response = await client.GetResponseAsync(query, options);
Console.WriteLine($"A: {response.Text}");
Console.WriteLine();
}
}
}