Repository navigation
Function Calling
Function calling lets Gemini call your own C# code to look up information or take actions. You describe your functions; the model decides when to call them; the SDK runs the call and sends the result back to the model, which then writes the final answer.
NuGet packages
dotnet add package Google_GenerativeAI
dotnet add package Google_GenerativeAI.ToolsGoogle_GenerativeAI.Tools brings in the CSharpToJsonSchema source generator, which provides the [FunctionTool] and [GenerateJsonSchema] attributes (namespace CSharpToJsonSchema).
| Style | Main types | NativeAOT / trimming | Best for |
|---|---|---|---|
| Reflection-based |
QuickTool, QuickTools
|
Needs a JsonSerializerContext (see NativeAOT and Trimming Guidelines) |
Prototyping, lambdas, third-party methods |
| Method-based (source generated) |
[FunctionTool] + generated Tools class |
Fully compatible | A few static or instance methods |
| Interface-based (source generated) |
[GenerateJsonSchema] + GenericFunctionTool
|
Fully compatible | Reusable tool sets with several methods |
| MCP servers | McpTool |
See MCP integration | Tools exposed by an external MCP server |
All of them end up as an IFunctionTool that you register with GenerativeModel.AddFunctionTool(...) (or ChatSession.AddFunctionTool(...)).
Wrap any delegate (lambda, static or instance method) at runtime.
using System.ComponentModel;
using GenerativeAI;
using GenerativeAI.Tools;
var func = async ([Description("Request to query student record")] QueryStudentRecordRequest query) =>
{
return new StudentRecord
{
StudentId = "12345",
FullName = query.FullName,
EnrolledCourses = new List<string> { "Math", "Physics", "Chemistry" },
Grades = new Dictionary<string, double> { { "Math", 95.0 }, { "Physics", 89.0 }, { "Chemistry", 88.0 } },
EnrollmentDate = new DateTime(2023, 1, 10),
IsActive = true
};
};
var quickTool = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
// For several delegates: new QuickTools(new Delegate[] { f1, f2, f3 })
var model = new GenerativeModel(apiKey, GoogleAIModels.Gemini38Flash);
model.AddFunctionTool(quickTool);
var result = await model.GenerateContentAsync(
"How is John Joe doing in senior grade? Enrollment year 01-01-2024 to 01-01-2025");
Console.WriteLine(result.Text);
public class QueryStudentRecordRequest
{
public string FullName { get; set; } = string.Empty;
public DateTime EnrollmentYearFrom { get; set; }
public DateTime EnrollmentYearTo { get; set; }
}
public class StudentRecord
{
public string StudentId { get; set; } = string.Empty;
public string FullName { get; set; } = string.Empty;
public List<string> EnrolledCourses { get; set; } = new();
public Dictionary<string, double> Grades { get; set; } = new();
public DateTime EnrollmentDate { get; set; }
public bool IsActive { get; set; }
}The QuickTool constructor is QuickTool(Delegate func, string? name = null, string? description = null, JsonSerializerOptions? options = null). Pass options (or register a resolver in DefaultSerializerOptions.CustomJsonTypeResolvers) when you publish with NativeAOT or trimming.
For Microsoft.Extensions.AI, convert with quickTool.AsMeaiTool() or quickTools.ToMeaiFunctions().
Mark methods with [FunctionTool]. The source generator creates a Tools class that wraps them, with no reflection at runtime.
using System.ComponentModel;
using CSharpToJsonSchema;
using GenerativeAI;
public static class BookTools
{
[FunctionTool(GoogleFunctionTool = true, MeaiFunctionTool = true)]
[Description("Get book page content")]
public static Task<string> GetBookPageContentAsync(string bookName, int bookPageNumber,
CancellationToken cancellationToken = default)
{
return Task.FromResult("There is nice weather outside, and I am stuck at home.");
}
}
// Gemini (GoogleFunctionTool = true)
var model = new GenerativeModel(apiKey, GoogleAIModels.Gemini38Flash);
var tools = new Tools([BookTools.GetBookPageContentAsync]);
model.AddFunctionTool(tools);
// Microsoft.Extensions.AI (MeaiFunctionTool = true)
chatOptions.Tools = tools.AsMeaiTools();-
GoogleFunctionTool = truemakes the generatedToolsusable withAddFunctionTool. -
MeaiFunctionTool = truegeneratesAsMeaiTools()for use withChatOptions.Tools(see Microsoft.Extensions.AI).
Put [GenerateJsonSchema] on an interface. Add [Description] attributes to methods and parameters so the model knows what they do.
using System.ComponentModel;
using CSharpToJsonSchema;
[GenerateJsonSchema(GoogleFunctionTool = true, MeaiFunctionTool = true)]
public interface IWeatherFunctions
{
[Description("Get the current weather in a given location")]
Weather GetCurrentWeather(
[Description("The city and state, e.g. San Francisco, CA")] string location,
Unit unit = Unit.Celsius);
[Description("Get the current weather in a given location asynchronously")]
Task<Weather> GetCurrentWeatherAsync(
[Description("The city and state, e.g. San Francisco, CA")] string location,
Unit unit = Unit.Celsius,
CancellationToken cancellationToken = default);
}
public enum Unit { Celsius, Fahrenheit, Imperial }
public class Weather
{
public string Location { get; set; } = string.Empty;
public double Temperature { get; set; }
public Unit Unit { get; set; }
public string Description { get; set; } = string.Empty;
}Both GoogleFunctionTool and MeaiFunctionTool are optional (default false):
-
GoogleFunctionTool = truegeneratesAsGoogleFunctionTool(). -
MeaiFunctionTool = truegeneratesAsMeaiTools().
public class WeatherService : IWeatherFunctions
{
public Weather GetCurrentWeather(string location, Unit unit = Unit.Celsius) =>
new() { Location = location, Temperature = 30.0, Unit = unit, Description = "Sunny" };
public Task<Weather> GetCurrentWeatherAsync(string location, Unit unit = Unit.Celsius,
CancellationToken cancellationToken = default) =>
Task.FromResult(new Weather { Location = location, Temperature = 22.0, Unit = unit, Description = "Sunny" });
}Replace the placeholder values with your real logic.
using GenerativeAI;
using GenerativeAI.Tools;
var service = new WeatherService();
var model = new GenerativeModel(apiKey, GoogleAIModels.Gemini38Flash);
// With GoogleFunctionTool = true:
model.AddFunctionTool(service.AsGoogleFunctionTool());
// Always available (works without GoogleFunctionTool = true):
model.AddFunctionTool(new GenericFunctionTool(service.AsTools(), service.AsCalls()));
// Microsoft.Extensions.AI, with MeaiFunctionTool = true:
chatOptions.Tools = service.AsMeaiTools();var result = await model.GenerateContentAsync("What is the weather in San Francisco today?");
Console.WriteLine(result.Text);The model reads your descriptions and decides whether to call a function. With the default settings the SDK runs the function, sends the result back, and returns the model's final answer.
Tools also work in a multi-turn chat:
var chat = model.StartChat();
chat.AddFunctionTool(service.AsGoogleFunctionTool());
var reply = await chat.GenerateContentAsync("Is it warmer in Paris or in Rome?");Use GenerativeModel.FunctionCallingBehaviour (type GenerativeAI.Core.FunctionCallingBehaviour) to control the automatic loop:
model.FunctionCallingBehaviour = new FunctionCallingBehaviour
{
FunctionEnabled = true, // send the tools to the model (default true)
AutoCallFunction = true, // run requested functions automatically (default true)
AutoReplyFunction = true, // send results back and return the model's final answer (default true)
AutoHandleBadFunctionCalls = false // try to recover from invalid function calls (default false)
};| Property | Default | Meaning |
|---|---|---|
FunctionEnabled |
true |
Turns function calling on or off. model.DisableFunctions() and model.EnableFunctions() are shortcuts. |
AutoCallFunction |
true |
If true, the SDK runs the function the model asks for. If false, the function call is returned in the response for you to handle. |
AutoReplyFunction |
true |
If true, the function result is sent back to the model and you get its final answer. If false, the function runs but the SDK returns the model's function-call response instead of sending the result back. |
AutoHandleBadFunctionCalls |
false |
If the model calls a function that no registered tool contains: false throws a GenerativeAIException; true sends an error result ("Invalid function name or function doesn't exist.") back to the model instead. |
When the model asks for several functions in one turn (parallel function calling), the SDK runs all of them and returns all results in a single turn.
You can also pass a ToolConfig and a behaviour per tool: model.AddFunctionTool(tool, toolConfig, functionCallingBehaviour). For example, to force the model to call one of your functions:
model.AddFunctionTool(tool, new ToolConfig
{
FunctionCallingConfig = new FunctionCallingConfig
{
Mode = FunctionCallingMode.ANY, // AUTO, ANY, NONE or VALIDATED
AllowedFunctionNames = new List<string> { "GetCurrentWeather" }
}
});Set AutoCallFunction = false to get the function call back and run it yourself. Send the result back as a FunctionResponse part in a turn with the user role:
using GenerativeAI;
using GenerativeAI.Types;
model.FunctionCallingBehaviour.AutoCallFunction = false;
var userTurn = new Content { Role = Roles.User };
userTurn.AddText("What's the weather in San Francisco?");
var request = new GenerateContentRequest { Contents = new List<Content> { userTurn } };
var response = await model.GenerateContentAsync(request);
var call = response.GetFunction(); // or response.GetFunctions() for parallel calls
if (call != null)
{
FunctionResponse? result = await tool.CallAsync(call); // any IFunctionTool, e.g. the GenericFunctionTool above
request.Contents.Add(response.Candidates![0].Content!); // the model's function-call turn
var resultTurn = new Content { Role = Roles.User }; // Gemini 3.x rejects the "function" role
resultTurn.AddPart(new Part { FunctionResponse = result });
request.Contents.Add(resultTurn);
var final = await model.GenerateContentAsync(request);
Console.WriteLine(final.Text);
}Since v4.0.0 the SDK sends function results in a user turn, as the official SDKs do, because Gemini 3.x models reject the legacy function role. Before each request, GenerativeModel converts any content with Role == Roles.Function to Roles.User, so chat histories saved by earlier SDK versions keep working without changes.
Interface-based ([GenerateJsonSchema]) and method-based ([FunctionTool]) tools
- Fully compatible with NativeAOT and trimming, because the schema and the call code are generated at compile time.
- Interface-based tools suit reusable tool sets with several methods: define the set once and reuse it across your solution. Use cases: plugin systems, microservice communication, domain-specific languages, data access layers, workflow automation, AI-driven data validation (validation rules defined as an interface), personalised learning platforms (one interface per learning module).
- Method-based tools suit a small number of class-scoped or static methods. Use cases: command-line integration, configuration management, hardware control, simple utility functions, real-time data analysis (wrapping single analysis methods), context-aware notifications.
Reflection-based QuickTool and QuickTools
- Not recommended when NativeAOT or trimming matters (or provide a
JsonSerializerContext, see NativeAOT and Trimming Guidelines). - Useful for calling functions from third-party libraries whose source you don't control, and for anonymous functions while testing prompts or prototyping.
- Use cases: dynamic scripting, rapid prototyping of API integrations, debugging and testing, event handling, data transformation pipelines, AI-powered data scraping, quickly creating input-validation tools, A/B testing of function logic.
In summary
- For performance and NativeAOT/trimming compatibility, use interface-based tools or method-based tools with
[FunctionTool]. - For rapid prototyping, testing or third-party libraries, use
QuickToolandQuickTools. - Use interface-based tools for reusable tool sets and method-based tools for a few methods.
Getting Started
- Initialization
- Authentication
- Configuration & Error Handling
- Supported Models
- Migration Guide (v4)
- Release Notes (v4)
Core Features
- Generate Content
- Chat Session
- Streaming
- Multimodal Features
- JSON Mode
- Function Calling
- Embeddings
- Context Caching
Gemini APIs
- Interactions API
- Multimodal Live API
- File Search
- Batch API
- Model Tuning
- Webhooks
- Agent APIs
- Image Generation
- Video Generation
- Speech Generation
- Files Client
- Model Client
Vertex AI
Integrations
Guides
Legacy