sakthivel-p-cse/codex/agui-dotnet-server-tools

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

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 protobuf.

Install

npx vskill@latest install sakthivel-p-cse/codex/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.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/sakthivel-p-cse/codex/agui-dotnet-server-tools/badge)](https://verified-skill.com/skills/sakthivel-p-cse/codex/agui-dotnet-server-tools)

Something wrong with this skill or its page? Tell us.