Shell | AI Integration
Shiny Shell’s source generation can produce metadata and navigation methods designed for AI tool calling via Microsoft.Extensions.AI. An AI chat client can discover your app’s routes, understand their purpose, extract parameters from natural language, and navigate to the correct page — all with generated AITool instances.
Prerequisites
Section titled “Prerequisites”- Install the
Microsoft.Extensions.AINuGet package:
dotnet add package Microsoft.Extensions.AI- Enable AI extensions in your
.csproj:
<PropertyGroup> <ShinyMauiShell_GenerateAiExtensions>true</ShinyMauiShell_GenerateAiExtensions></PropertyGroup>How It Works
Section titled “How It Works”The AI integration builds on the same [ShellMap] and [ShellProperty] attributes you already use. Add description parameters that explain user intent rather than just naming the page, and the source generator produces everything the AI needs.
The flow:
- AI calls
GetAiToolApplicableGeneratedRoutes()to discover what pages exist and what they do - AI matches user intent to the right route based on descriptions
- AI extracts parameter values from natural language
- AI calls
NavigateToRoute()with the route and parameters - The app navigates and the form is pre-populated
Describing Routes for AI
Section titled “Describing Routes for AI”The description parameter on [ShellMap] tells the AI when to use this route. Write it as intent signals, not page names:
// Good — describes user intent[ShellMap<WorkOrderPage>(description: "Use when the user reports something broken, malfunctioning, needing repair, or any physical problem that needs to be fixed")]
// Bad — just names the page[ShellMap<WorkOrderPage>(description: "Work order page")]Describing Parameters for AI
Section titled “Describing Parameters for AI”The description parameter on [ShellProperty] tells the AI how to infer the value from what the user said:
// Good — tells AI to extract and infer[ShellProperty("Summarize what is broken based on what the user said", required: true)]public string Description { get; set; } = string.Empty;
[ShellProperty("Infer urgency from the user's tone. Must be: Low, Medium, High, or Urgent", required: true)]public WorkOrderPriority Priority { get; set; } = WorkOrderPriority.Medium;
// Bad — just names the field[ShellProperty("The description")]public string Description { get; set; } = string.Empty;Supported Property Types
Section titled “Supported Property Types”NavigateToRoute accepts Dictionary<string, string> from the AI and automatically converts string values to the correct property type. The following types are supported:
| Type | Conversion |
|---|---|
string | Direct assignment |
int, long, short, byte | T.Parse(value) |
float, double, decimal | T.Parse(value) |
bool | bool.Parse(value) |
Guid, DateTime, DateTimeOffset, TimeSpan | T.Parse(value) |
Uri | new Uri(value) |
| Enums | Enum.Parse(typeof(T), value, true) — case-insensitive |
| Other types | Convert.ChangeType fallback |
Enums work especially well with AI — the model naturally outputs the enum member name as a string (e.g. "Urgent"), and the generator handles the parsing with case-insensitive matching.
Complete AI-Compatible ViewModel
Section titled “Complete AI-Compatible ViewModel”public enum WorkOrderPriority { Low, Medium, High, Urgent }
[ShellMap<WorkOrderPage>(description: "Use when the user reports something broken, malfunctioning, needing repair, maintenance, or service")]public partial class WorkOrderViewModel(INavigator navigator) : ObservableObject{ [ShellProperty("Summarize what is broken based on what the user said", required: true)] public string Description { get; set; } = string.Empty;
[ShellProperty("Infer urgency from the user's tone. Must be: Low, Medium, High, or Urgent", required: true)] public WorkOrderPriority Priority { get; set; } = WorkOrderPriority.Medium;
[ShellProperty("The physical location if the user mentioned one, otherwise leave empty", required: false)] public string Location { get; set; } = string.Empty;}## Generated AI Extensions
When AI extensions are enabled, the source generator produces the following members in `AiExtensions.g.cs`:
### GetAiToolApplicableGeneratedRoutes
Returns only routes that have a `description` on `[ShellMap]` **and** at least one `[ShellProperty]`. These are routes an AI can meaningfully discover and navigate to.
```csharp[Description("This provides a list of AI tool applicable routes")]public static GeneratedRouteInfo[] GetAiToolApplicableGeneratedRoutes(this INavigator navigator);AiRoutePrompt
Section titled “AiRoutePrompt”A pre-formatted string describing all AI-applicable routes, their descriptions, and parameters. Designed to be included in an AI system message so the model knows which routes are available without calling a discovery tool first.
public static string AiRoutePrompt(this INavigator navigator);NavigateToRoute
Section titled “NavigateToRoute”AI-friendly navigation that accepts Dictionary<string, string> and dispatches to NavigateTo<TViewModel> with direct property setters. String values are automatically converted to the target property type (see Supported Property Types). Returns a confirmation message string.
[Description("Navigate to a route in the application, passing parameters as key-value pairs. Returns a confirmation message.")]public static Task<string> NavigateToRoute( this INavigator navigator, [Description("The route name to navigate to")] string route, [Description("Route parameters as key-value pairs")] Dictionary<string, string>? args = null);GetAiTools
Section titled “GetAiTools”Returns ready-to-use AITool instances for route discovery and navigation. This is the recommended way to wire up AI tools — no manual AIFunctionFactory.Create calls needed.
public static IList<AITool> GetAiTools(this INavigator navigator);GeneratedRouteInfo Types
Section titled “GeneratedRouteInfo Types”namespace Shiny.Infrastructure;
public record GeneratedRouteInfo( string Route, // Route name from [ShellMap] string Description, // Description from [ShellMap] (empty if not provided) GeneratedRouteParameter[] Parameters // All [ShellProperty] properties);
public record GeneratedRouteParameter( string ParameterName, // Property name — used as key in NavigateToRoute args string Description, // From [ShellProperty("...")] — empty if not provided string TypeName, // CLR type: "string", "int", "bool", etc. bool IsRequired // From [ShellProperty(required: ...)]);Wiring Up AI Tools
Section titled “Wiring Up AI Tools”Use the generated GetAiTools() method — it returns ready-to-use AITool instances:
using Microsoft.Extensions.AI;
var tools = navigator.GetAiTools();var options = new ChatOptions { Tools = [.. tools] };var response = await chatClient.GetResponseAsync(history, options);You can also seed the AI with route info upfront via AiRoutePrompt, so the model knows what routes are available without making a discovery call first:
history.Add(new ChatMessage(ChatRole.System, $"You are a helpful assistant. {navigator.AiRoutePrompt()}"));Why Two Tools?
Section titled “Why Two Tools?”The two-tool design (discover + navigate) scales to any number of routes without registering a separate tool per page. The AI:
- Calls
GetRoutes(backed byGetAiToolApplicableGeneratedRoutes) to see all available pages, their descriptions, and parameter schemas - Matches the user’s intent to the right route based on descriptions
- Calls
NavigateToRoutewith the extracted route name and parameters
This avoids registering a generated NavigateToWorkOrder(...) tool for every page — which doesn’t scale and pollutes the tool list.
What Makes a Route “AI Applicable”?
Section titled “What Makes a Route “AI Applicable”?”GetAiToolApplicableGeneratedRoutes filters to routes that meet both criteria:
- The
[ShellMap]has a non-nulldescription - The ViewModel has at least one
[ShellProperty]
Routes like your home page or settings page that don’t need AI parameter filling are excluded automatically. This keeps the AI’s tool response focused on pages it can actually act on.
Configuring AI Extensions
Section titled “Configuring AI Extensions”AI extensions are disabled by default and can be enabled or customized via MSBuild properties in your .csproj:
<PropertyGroup> <!-- Enable AI extensions (disabled by default, requires Microsoft.Extensions.AI) --> <ShinyMauiShell_GenerateAiExtensions>true</ShinyMauiShell_GenerateAiExtensions>
<!-- Customize the generated class name (default: AiExtensions) --> <ShinyMauiShell_AiExtensionsClassName>MyAppRouteExtensions</ShinyMauiShell_AiExtensionsClassName>
<!-- Customize the AI navigate method name (default: NavigateToRoute) --> <ShinyMauiShell_AiNavigateMethodName>GoToPage</ShinyMauiShell_AiNavigateMethodName></PropertyGroup>| Property | Default | Controls |
|---|---|---|
ShinyMauiShell_GenerateAiExtensions | false | GetAiToolApplicableGeneratedRoutes, NavigateToRoute, GetAiTools(), and AiRoutePrompt. Requires Microsoft.Extensions.AI (SHINY003 error if missing) |
ShinyMauiShell_AiExtensionsClassName | AiExtensions | Class name for the route info/AI extensions class |
ShinyMauiShell_AiNavigateMethodName | NavigateToRoute | Method name for the AI-friendly navigate method |
Tips for Better AI Descriptions
Section titled “Tips for Better AI Descriptions”| Do | Don’t |
|---|---|
| Describe user intent signals | Name the page |
| ”Use when the user reports something broken" | "Work order form” |
| Tell AI to infer from context | Expect exact field values |
| ”Infer urgency from tone" | "The priority level” |
| Make optional fields truly optional | Require everything |
| ”Leave empty if not mentioned” | Require the user to state everything |