VOOZH about

URL: https://shinylib.net/mediator/extensions/ai/

⇱ AI Tools | Shiny.NET


Skip to content
Client v5: BLE, BLE Hosting, HTTP, Jobs - Linux, MacOS, & Blazor Support! Full AOT, RX on BLE only & MANY other features! Power up!

AI Tools

Shiny Mediator can expose your request and command contracts as AI-callable tools via Microsoft.Extensions.AI. The source generator discovers contracts annotated with [Description] and generates AIFunction wrappers with JSON schemas automatically.

  1. Install the Microsoft.Extensions.AI NuGet package:
Terminal window
dotnetaddpackageMicrosoft.Extensions.AI
  1. Enable AI tool generation in your .csproj:
<PropertyGroup>
<ShinyMediatorGenerateAITools>true</ShinyMediatorGenerateAITools>
</PropertyGroup>

Add [Description] (from System.ComponentModel) to your contracts and their properties. Only contracts that implement IRequest<T> or ICommand and have a [Description] attribute will be picked up.

usingSystem.ComponentModel;
[Description("Gets the weather forecast for a given city")]
publicrecordGetWeatherRequest(
[property: Description("The city name to get weather for")]
stringCity
) : IRequest<WeatherResult>;
[Description("Performs a mathematical calculation")]
publicrecordCalculateRequest(
[property: Description("First operand")]
doubleA,
[property: Description("Math operator: +, -, *, /")]
stringOperator,
[property: Description("Second operand")]
doubleB
) : IRequest<double>;
[Description("Sends a notification to a user")]
publicrecordSendNotificationCommand(
[property: Description("The user ID to notify")]
intUserId,
[property: Description("The notification message")]
stringMessage
) : ICommand;

Register the generated AI tools with the mediator builder:

builder.Services.AddShinyMediator(cfg=>
{
cfg.AddMediatorRegistry();
cfg.AddGeneratedAITools();
});

This registers each annotated contract as an AITool singleton in the DI container.

Retrieve the tools from DI and pass them to any IChatClient:

usingMicrosoft.Extensions.AI;
vartools= host.Services.GetServices<AITool>().ToList();
varoptions=newChatOptions { Tools = tools };
varresponse=await chatClient.GetResponseAsync(history, options);

For automatic tool invocation (the AI calls your tools and the client handles the round-trip), use UseFunctionInvocation():

IChatClientchatClient= openAiClient
.GetChatClient("gpt-4.1")
.AsIChatClient()
.AsBuilder()
.UseFunctionInvocation()
.Build();

For each contract with [Description] that implements IRequest<T> or ICommand, the source generator produces:

  1. An AIFunction class that wraps the mediator call with a JSON schema built from the contract’s properties
  2. AddGeneratedAITools() extension method on ShinyMediatorBuilder that registers all generated tools as AITool singletons

The generated JSON schema includes property types, descriptions, required/optional fields, enum values, and default values.

  • IRequest<T> contracts return the result to the AI
  • ICommand contracts return "Command executed successfully" to the AI
TypeJSON Schema Type
string, Guid, Uri, DateTime, DateTimeOffset, DateOnlystring
boolboolean
int, long, short, byteinteger
float, double, decimalnumber
Enumsstring with enum values
Arrays / IEnumerable<T>array
Complex typesobject

Nullable properties and properties with default values are marked as optional in the schema. Non-nullable properties without defaults are required.

Here is a complete example using GitHub Copilot as the AI backend:

usingSystem.ComponentModel;
usingMicrosoft.Extensions.AI;
usingMicrosoft.Extensions.DependencyInjection;
usingMicrosoft.Extensions.Hosting;
// Build host with mediator + AI tools
varbuilder= Host.CreateApplicationBuilder(args);
builder.Services.AddShinyMediator(cfg=>
{
cfg.AddMediatorRegistry();
cfg.AddGeneratedAITools();
});
varhost= builder.Build();
// Get AI tools from DI
vartools= host.Services.GetServices<AITool>().ToList();
// Use with any IChatClient
varoptions=newChatOptions { Tools = tools };
varhistory=newList<ChatMessage>();
history.Add(newChatMessage(ChatRole.User, "What's the weather in Seattle?"));
varresponse=await chatClient.GetResponseAsync(history, options);
[Description("Gets the current weather forecast for a city")]
publicrecordGetWeatherRequest(
[property: Description("The city to get weather for")]
stringCity
) : IRequest<string>;
[MediatorSingleton]
publicclassGetWeatherHandler : IRequestHandler<GetWeatherRequest, string>
{
publicTask<string> Handle(
GetWeatherRequestrequest,
IMediatorContextcontext,
CancellationTokenct)
{
return Task.FromResult($"Weather in {request.City}: 72F and sunny");
}
}

When the AI receives “What’s the weather in Seattle?”, it will call the GetWeatherRequest tool with City: "Seattle", the mediator routes it to the handler, and the result is returned to the AI.