Expose client-side (frontend) tools to an AG-UI agent with the AG-UI .NET SDK — C# functions that run in the CLIENT app (read local state, GPS, UI, device APIs), where the client executes the call and returns the result so the run continues. USE FOR: declaring an AIFunction in the client and passing it via ChatOptions.Tools to AGUIChatClient; having the client execute a tool the model requested and feed the result back automatically; understanding why no UseFunctionInvocation is needed on the client (AGUIChatClient already invokes functions); what the server must do (UseFunctionInvocation + TerminateOnUnknownCalls) so it forwards an unknown/client tool instead of erroring. DO NOT USE FOR: tools that run on the server/backend (use agui-dotnet-server-tools); pausing for human approval/input before acting (interrupts / human-in-the-loop); plain chat (use agui-dotnet-streaming-chat); shared state, generative UI, multimodal, or protobuf.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Goal: let the model call a function that runs in the client app — to read something only the client has (location, local state, UI selection, a device API) — with the client executing the call and returning the result so the run continues to a final answer.
A client tool is an ordinary Microsoft.Extensions.AI AIFunction you attach to the request via ChatOptions.Tools. AGUIChatClient is itself a function-invoking client, so it runs the tool locally and sends the result back automatically — you do not wrap it in UseFunctionInvocation.
dotnet add package AGUI.ClientMicrosoft.Extensions.AI supplies AIFunctionFactory, ChatOptions, and AITool. Run dotnet package search AGUI.Client --exact-match for the current version.
Define the function in the client, wrap it with AIFunctionFactory.Create, and put it on ChatOptions.Tools:
using System.ComponentModel;
using AGUI.Client;
using Microsoft.Extensions.AI;
[Description("Get the user's current location from GPS.")]
static string GetUserLocation() => "Amsterdam, Netherlands (52.37°N, 4.90°E)";
using var httpClient = new HttpClient();
IChatClient client = new AGUIChatClient(new(httpClient, "http://localhost:5003"));
var options = new ChatOptions { Tools = [AIFunctionFactory.Create(GetUserLocation)] };
var messages = new List<ChatMessage> { new(ChatRole.User, "What's fun to do near me?") };
await foreach (var update in client.GetStreamingResponseAsync(messages, options))
{
Console.Write(update.Text);
}When the model decides to call GetUserLocation, AGUIChatClient invokes it on this machine, returns the result to the server, and the run continues — the streamed text you print already reflects the location.
The server forwards a tool it doesn't own to the client instead of trying to run it. Register the server's chat client with function invocation and TerminateOnUnknownCalls so an unknown (client) tool ends the server turn cleanly and is surfaced to the client to execute:
builder.Services.AddChatClient(/* provider IChatClient */)
.UseFunctionInvocation(fic => fic.TerminateOnUnknownCalls = true);The server declares none of the client's tools; it only needs to forward them.
UseFunctionInvocation. AGUIChatClient already invokes functions; adding another function-invoking layer double-handles the call and can execute it twice or strip the AG-UI tool routing. Pass the tool through ChatOptions.Tools and let the client invoke it.RUN_FINISHED in a single client call — you wrote no code to send the tool result back.34c3e0c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.