> Expose server-side (backend) tools an AG-UI agent can call with the AG-UI .NET SDK — C# functions that run on the server, where the server executes the call and feeds the result back to the model. USE FOR: defining an AIFunction with AIFunctionFactory.Create and registering it on the server's IChatClient via ConfigureOptions + UseFunctionInvocation; making the model call your backend function during a run; AOT-safe tool arguments/results (registering a JsonSerializerContext for complex tool...
Scanned 9/12/2026
Install to Claude Code
npx -y skills add thedixitjain/the-mega-skill-library --skill agui-dotnet-server-tools --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Agui Dotnet Server Tools?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/thedixitjain-agui-dotnet-server-tools)More formats (shields.io, HTML) on the badges page.
---
name: agui-dotnet-server-tools
description: "> Expose server-side (backend) tools an AG-UI agent can call with the AG-UI .NET SDK — C# functions that run on the server, where the server executes the call and feeds the result back to the model. USE FOR: defining an AIFunction with AIFunctionFactory.Create and registering it on the server's IChatClient via ConfigureOptions + UseFunctionInvocation; making the model call your backend function during a run; AOT-safe tool arguments/results (registering a JsonSerializerContext for complex tool parameter types); parallel/concurrent backend tool calls (AllowConcurrentInvocation); TerminateOnUnknownCalls behavior. DO NOT USE FOR: tools that run in the client/frontend (use agui-dotnet-client-tools); pausing a tool for human approval or input (interrupts / human-in-the-loop); plain streaming chat with no tools (use agui-dotnet-streaming-chat); shared state, generative UI, multimodal, or..."
category: backend-and-data
source_repo: ag-ui-protocol/ag-ui
source_path: "sdks/dotnet/plugins/ag-ui-dotnet/skills/agui-dotnet-server-tools/SKILL.md"
source_url: https://github.com/ag-ui-protocol/ag-ui/blob/HEAD/sdks/dotnet/plugins/ag-ui-dotnet/skills/agui-dotnet-server-tools/SKILL.md
---
# AG-UI .NET — server (backend) tools
Goal: let the model call a C# function that runs on your server, with the server executing the call and returning the result so the run continues to a final answer.
A server tool is an ordinary `Microsoft.Extensions.AI` `AIFunction` registered on the server's `IChatClient`. The `FunctionInvokingChatClient` executes it inside the run; the client sends no tools and needs no tool code, and receives the final answer. Tool-call and result events still cross the wire — the client just never executes anything.
## Install
```bash
dotnet add package AGUI.Server
dotnet add package AGUI.Formatting
```
`Microsoft.Extensions.AI` supplies `AIFunctionFactory`, `AddChatClient`, and `UseFunctionInvocation`. Run `dotnet package search AGUI.Server --exact-match` for the current version.
## Define and register a tool
Create the function with `AIFunctionFactory.Create`, add it to the chat client's tools, and enable function invocation:
```csharp
using System.ComponentModel;
using Microsoft.Extensions.AI;
[Description("Search for restaurants in a location.")]
static RestaurantSearchResponse SearchRestaurants(
[Description("Where to search and what cuisine.")] RestaurantSearchRequest request)
{
// ... real lookup ...
}
var builder = WebApplication.CreateBuilder(args);
var searchRestaurants = AIFunctionFactory.Create(
SearchRestaurants,
serializerOptions: SampleJsonSerializerContext.Default.Options);
builder.Services.AddChatClient(
new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetChatClient(deploymentName)
.AsIChatClient())
.ConfigureOptions(o => (o.Tools ??= []).Add(searchRestaurants))
.UseFunctionInvocation(fic => fic.TerminateOnUnknownCalls = true);
```
The `[Description]` attributes become the tool and parameter schema the model sees. Any `Microsoft.Extensions.AI` provider works in place of Azure OpenAI.
## AOT-safe tool arguments and results
When a tool takes or returns a complex type (anything beyond primitives), its schema and (de)serialization must be source-generated, not reflection-based. Put the parameter and result types in a `JsonSerializerContext`, register it on the host's JSON options, and pass it to `AIFunctionFactory.Create`:
```csharp
[JsonSerializable(typeof(RestaurantSearchRequest))]
[JsonSerializable(typeof(RestaurantSearchResponse))]
internal sealed partial class SampleJsonSerializerContext : JsonSerializerContext;
builder.Services.ConfigureHttpJsonOptions(o =>
o.SerializerOptions.TypeInfoResolverChain.Add(SampleJsonSerializerContext.Default));
var searchRestaurants = AIFunctionFactory.Create(
SearchRestaurants,
serializerOptions: SampleJsonSerializerContext.Default.Options);
```
The same context is registered on the host (so the wire `RunAgentInput` round-trips the types) and passed to the function (so its argument binding uses source-gen).
## Host the endpoint
The endpoint streams the registered `IChatClient` — function calls are resolved server-side inside the stream before any text reaches the client:
```csharp
using AGUI.Abstractions;
using AGUI.Formatting;
using AGUI.Server;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Options;
using JsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions;
builder.Services.AddSingleton<IAGUIEventStreamFormatter, SseEventStreamFormatter>();
var app = builder.Build();
app.MapPost("/", async (
[FromBody] RunAgentInput input,
IChatClient chatClient,
IAGUIEventStreamFormatter formatter,
IOptions<JsonOptions> jsonOptions,
HttpContext http,
CancellationToken ct) =>
{
var ctx = input.ToChatRequestContext(jsonOptions.Value.SerializerOptions);
var updates = chatClient.GetStreamingResponseAsync(ctx.Messages, ctx.ChatOptions, ct);
var events = updates.AsAGUIEventStreamAsync(ctx, ct);
http.Response.ContentType = formatter.MediaType;
http.Response.Headers.CacheControl = "no-cache";
await formatter.WriteAsync(events, http.Response.Body, ct);
});
app.Run();
```
## Parallel tool calls
When the model requests several tool calls in one turn, let the function-invoking client run them concurrently:
```csharp
.UseFunctionInvocation(fic =>
{
fic.TerminateOnUnknownCalls = true;
fic.AllowConcurrentInvocation = true;
});
```
The model decides whether to batch calls; `AllowConcurrentInvocation` only controls whether the already-requested calls execute in parallel instead of serially.
## Anti-patterns
- **Registering a tool with complex parameters but no source-gen context.** Reflection-based schema generation breaks under Native AOT and trimming. Every non-primitive tool argument or result type needs a `JsonSerializerContext` entry, registered on the host and passed to `AIFunctionFactory.Create`.
- **Leaving `TerminateOnUnknownCalls` at its default when client tools are also in play.** With it set, a tool the server doesn't own (one the client declared) ends the server run cleanly so the client can execute it; without it the unknown call surfaces as an error mid-run.
## Verify
1. Send a prompt that needs the tool and watch the stream: a `TOOL_CALL_START` / `TOOL_CALL_ARGS` / `TOOL_CALL_END` followed by a `TOOL_CALL_RESULT`, then assistant text that uses the result — all within one run that ends `RUN_FINISHED`.
2. The client receives the final answer with no tool code of its own.
3. If targeting Native AOT, `dotnet publish` produces no trim/AOT warnings for the tool types.
---
**Source:** [`ag-ui-protocol/ag-ui`](https://github.com/ag-ui-protocol/ag-ui) → `sdks/dotnet/plugins/ag-ui-dotnet/skills/agui-dotnet-server-tools/SKILL.md`
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!